/src/opensc/src/libopensc/card-atrust-acos.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * atrust-acos.c: Support for A-Trust ACOS based cards |
3 | | * |
4 | | * Copyright (C) 2005 Franz Brandl <brandl@a-trust.at> based on work from |
5 | | * Jörn Zukowski <zukowski@trustcenter.de> and |
6 | | * Nils Larsch <larsch@trustcenter.de>, TrustCenter AG |
7 | | * |
8 | | * This library is free software; you can redistribute it and/or |
9 | | * modify it under the terms of the GNU Lesser General Public |
10 | | * License as published by the Free Software Foundation; either |
11 | | * version 2.1 of the License, or (at your option) any later version. |
12 | | * |
13 | | * This library is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | | * Lesser General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU Lesser General Public |
19 | | * License along with this library; if not, write to the Free Software |
20 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | | */ |
22 | | |
23 | | #ifdef HAVE_CONFIG_H |
24 | | #include "config.h" |
25 | | #endif |
26 | | |
27 | | #include <stdlib.h> |
28 | | #include <string.h> |
29 | | |
30 | | #include "internal.h" |
31 | | #include "asn1.h" |
32 | | #include "cardctl.h" |
33 | | |
34 | | /*****************************************************************************/ |
35 | | |
36 | | #define ACOS_EMV_A03 "A-TRUST ACOS" |
37 | 0 | #define ACOS_EMV_A05 "A-TRUST ACOS A05" |
38 | | |
39 | | static const char *atrust_acos_atrs[] = { |
40 | | "3B:BF:11:00:81:31:fe:45:45:50:41", |
41 | | "3B:BF:11:00:81:31:fe:45:4d:43:41", |
42 | | "3B:BF:13:00:81:31:fe:45:45:50:41", |
43 | | "3B:BF:13:00:81:31:fe:45:4d:43:41", |
44 | | NULL |
45 | | }; |
46 | | |
47 | | /* sequence and number has to match atr table ! */ |
48 | | static const char *atrust_acos_names[] = { |
49 | | ACOS_EMV_A03, |
50 | | ACOS_EMV_A03, |
51 | | ACOS_EMV_A05, |
52 | | ACOS_EMV_A05, |
53 | | NULL |
54 | | }; |
55 | | |
56 | | static struct sc_card_operations atrust_acos_ops; |
57 | | static struct sc_card_operations *iso_ops = NULL; |
58 | | |
59 | | static struct sc_card_driver atrust_acos_drv = { |
60 | | "A-Trust ACOS cards", |
61 | | "atrust-acos", |
62 | | &atrust_acos_ops, |
63 | | NULL, 0, NULL |
64 | | }; |
65 | | |
66 | | /* internal structure to save the current security environment */ |
67 | | typedef struct atrust_acos_ex_data_st { |
68 | | int sec_ops; /* the currently selected security operation, |
69 | | * i.e. SC_SEC_OPERATION_AUTHENTICATE etc. */ |
70 | | unsigned long fix_digestInfo; |
71 | | } atrust_acos_ex_data; |
72 | | |
73 | | /*****************************************************************************/ |
74 | | |
75 | | static int atrust_acos_match_card(struct sc_card *card) |
76 | 0 | { |
77 | 0 | int i, match = 0; |
78 | | |
79 | |
|
80 | 0 | for (i = 0; atrust_acos_atrs[i] != NULL; i++) |
81 | 0 | { |
82 | 0 | u8 defatr[SC_MAX_ATR_SIZE]; |
83 | 0 | size_t len = sizeof(defatr); |
84 | 0 | const char *atrp = atrust_acos_atrs[i]; |
85 | |
|
86 | 0 | if (sc_hex_to_bin(atrp, defatr, &len)) |
87 | 0 | continue; |
88 | | /* we may only verify part of ATR since */ |
89 | | /* part of the hist chars is variable */ |
90 | 0 | if (len > card->atr.len) |
91 | 0 | continue; |
92 | 0 | if (memcmp(card->atr.value, defatr, len) != 0) |
93 | 0 | continue; |
94 | | |
95 | 0 | match = 1; |
96 | 0 | card->name = atrust_acos_names[i]; |
97 | |
|
98 | 0 | break; |
99 | 0 | } |
100 | 0 | return match; |
101 | 0 | } |
102 | | |
103 | | /*****************************************************************************/ |
104 | | |
105 | | static int atrust_acos_init(struct sc_card *card) |
106 | 0 | { |
107 | 0 | unsigned int flags; |
108 | 0 | atrust_acos_ex_data *ex_data; |
109 | |
|
110 | 0 | ex_data = calloc(1, sizeof(atrust_acos_ex_data)); |
111 | 0 | if (ex_data == NULL) |
112 | 0 | return SC_ERROR_OUT_OF_MEMORY; |
113 | | |
114 | 0 | card->cla = 0x00; |
115 | 0 | card->drv_data = (void *)ex_data; |
116 | | |
117 | | /* set the supported algorithm */ |
118 | |
|
119 | 0 | flags = SC_ALGORITHM_RSA_PAD_PKCS1 |
120 | 0 | | SC_ALGORITHM_RSA_HASH_NONE |
121 | 0 | | SC_ALGORITHM_RSA_HASH_SHA1 |
122 | 0 | | SC_ALGORITHM_RSA_HASH_MD5 |
123 | 0 | | SC_ALGORITHM_RSA_HASH_RIPEMD160 |
124 | 0 | | SC_ALGORITHM_RSA_HASH_MD5_SHA1; |
125 | |
|
126 | 0 | if (card->name != NULL && !strcmp(card->name, ACOS_EMV_A05)) |
127 | 0 | flags |= SC_ALGORITHM_RSA_HASH_SHA256; |
128 | |
|
129 | 0 | _sc_card_add_rsa_alg(card, 1536, flags, 0x10001); |
130 | | |
131 | | /* we need read_binary&friends with max 128 bytes per read */ |
132 | 0 | card->max_send_size = 128; |
133 | 0 | card->max_recv_size = 128; |
134 | |
|
135 | 0 | return 0; |
136 | 0 | } |
137 | | |
138 | | /*****************************************************************************/ |
139 | | |
140 | | static int atrust_acos_finish(struct sc_card *card) |
141 | 0 | { |
142 | 0 | if (card->drv_data) |
143 | 0 | free((atrust_acos_ex_data *)card->drv_data); |
144 | 0 | return 0; |
145 | 0 | } |
146 | | |
147 | | /*****************************************************************************/ |
148 | | |
149 | | static int process_fci(struct sc_context *ctx, struct sc_file *file, |
150 | | const u8 *buf, size_t buflen) |
151 | 0 | { |
152 | |
|
153 | 0 | size_t taglen, len = buflen; |
154 | 0 | const u8 *tag = NULL, *p; |
155 | |
|
156 | 0 | sc_log(ctx, "processing FCI bytes\n"); |
157 | |
|
158 | 0 | if (buflen < 2) |
159 | 0 | return SC_ERROR_INTERNAL; |
160 | 0 | if (buf[0] != 0x6f) /* FCI template */ |
161 | 0 | return SC_ERROR_INVALID_DATA; |
162 | 0 | len = (size_t)buf[1]; |
163 | 0 | if (buflen - 2 < len) |
164 | 0 | return SC_ERROR_INVALID_DATA; |
165 | 0 | p = buf + 2; |
166 | | |
167 | | /* defaults */ |
168 | 0 | file->type = SC_FILE_TYPE_WORKING_EF; |
169 | 0 | file->ef_structure = SC_FILE_EF_UNKNOWN; |
170 | 0 | file->shareable = 0; |
171 | 0 | file->record_length = 0; |
172 | 0 | file->size = 0; |
173 | | |
174 | | /* get file size */ |
175 | 0 | tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); |
176 | 0 | if (tag != NULL && taglen >= 2) { |
177 | 0 | int bytes = (tag[0] << 8) + tag[1]; |
178 | 0 | sc_log(ctx, " bytes in file: %d\n", bytes); |
179 | 0 | file->size = bytes; |
180 | 0 | } |
181 | | |
182 | | /* get file type */ |
183 | 0 | tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen); |
184 | 0 | if (tag != NULL) { |
185 | 0 | const char *type = "unknown"; |
186 | 0 | const char *structure = "unknown"; |
187 | |
|
188 | 0 | if (taglen == 1 && tag[0] == 0x01) { |
189 | | /* transparent EF */ |
190 | 0 | type = "working EF"; |
191 | 0 | structure = "transparent"; |
192 | 0 | file->type = SC_FILE_TYPE_WORKING_EF; |
193 | 0 | file->ef_structure = SC_FILE_EF_TRANSPARENT; |
194 | 0 | } else if (taglen == 1 && tag[0] == 0x11) { |
195 | | /* object EF */ |
196 | 0 | type = "working EF"; |
197 | 0 | structure = "object"; |
198 | 0 | file->type = SC_FILE_TYPE_WORKING_EF; |
199 | 0 | file->ef_structure = SC_FILE_EF_TRANSPARENT; /* TODO */ |
200 | 0 | } else if (taglen == 3 && tag[1] == 0x21) { |
201 | 0 | type = "working EF"; |
202 | 0 | file->record_length = tag[2]; |
203 | 0 | file->type = SC_FILE_TYPE_WORKING_EF; |
204 | | /* linear fixed, cyclic or compute */ |
205 | 0 | switch ( tag[0] ) |
206 | 0 | { |
207 | 0 | case 0x02: |
208 | 0 | structure = "linear fixed"; |
209 | 0 | file->ef_structure = SC_FILE_EF_LINEAR_FIXED; |
210 | 0 | break; |
211 | 0 | case 0x07: |
212 | 0 | structure = "cyclic"; |
213 | 0 | file->ef_structure = SC_FILE_EF_CYCLIC; |
214 | 0 | break; |
215 | 0 | case 0x17: |
216 | 0 | structure = "compute"; |
217 | 0 | file->ef_structure = SC_FILE_EF_UNKNOWN; |
218 | 0 | break; |
219 | 0 | default: |
220 | 0 | structure = "unknown"; |
221 | 0 | file->ef_structure = SC_FILE_EF_UNKNOWN; |
222 | 0 | file->record_length = 0; |
223 | 0 | break; |
224 | 0 | } |
225 | 0 | } |
226 | | |
227 | 0 | sc_log(ctx, " type: %s\n", type); |
228 | 0 | sc_log(ctx, " EF structure: %s\n", structure); |
229 | 0 | } |
230 | 0 | file->magic = SC_FILE_MAGIC; |
231 | |
|
232 | 0 | return SC_SUCCESS; |
233 | 0 | } |
234 | | |
235 | | /*****************************************************************************/ |
236 | | |
237 | | static int atrust_acos_select_aid(struct sc_card *card, |
238 | | u8 aid[16], size_t len, |
239 | | struct sc_file **file_out) |
240 | 0 | { |
241 | 0 | sc_apdu_t apdu; |
242 | 0 | int r; |
243 | 0 | size_t i = 0; |
244 | |
|
245 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x04, 0x0C); |
246 | 0 | apdu.lc = len; |
247 | 0 | apdu.data = (u8*)aid; |
248 | 0 | apdu.datalen = len; |
249 | 0 | apdu.resplen = 0; |
250 | 0 | apdu.le = 0; |
251 | 0 | r = sc_transmit_apdu(card, &apdu); |
252 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
253 | | |
254 | | /* check return value */ |
255 | 0 | if (!(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) && apdu.sw1 != 0x61) |
256 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); |
257 | | |
258 | 0 | if (file_out) { |
259 | 0 | sc_file_t *file = sc_file_new(); |
260 | 0 | if (!file) |
261 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); |
262 | 0 | file->type = SC_FILE_TYPE_DF; |
263 | 0 | file->ef_structure = SC_FILE_EF_UNKNOWN; |
264 | 0 | file->path.len = 0; |
265 | 0 | file->size = 0; |
266 | | /* AID */ |
267 | 0 | for (i = 0; i < len; i++) |
268 | 0 | file->name[i] = aid[i]; |
269 | 0 | file->namelen = len; |
270 | 0 | file->id = 0x0000; |
271 | 0 | file->magic = SC_FILE_MAGIC; |
272 | 0 | *file_out = file; |
273 | 0 | } |
274 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); |
275 | 0 | } |
276 | | |
277 | | /*****************************************************************************/ |
278 | | |
279 | | static int atrust_acos_select_fid(struct sc_card *card, |
280 | | unsigned int id_hi, unsigned int id_lo, |
281 | | struct sc_file **file_out) |
282 | 0 | { |
283 | 0 | sc_apdu_t apdu; |
284 | 0 | u8 data[] = {id_hi & 0xff, id_lo & 0xff}; |
285 | 0 | u8 resp[SC_MAX_APDU_BUFFER_SIZE]; |
286 | 0 | int bIsDF = 0, r; |
287 | | |
288 | | /* request FCI to distinguish between EFs and DFs */ |
289 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x00); |
290 | 0 | apdu.resp = (u8*)resp; |
291 | 0 | apdu.resplen = SC_MAX_APDU_BUFFER_SIZE; |
292 | 0 | apdu.le = 256; |
293 | 0 | apdu.lc = 2; |
294 | 0 | apdu.data = (u8*)data; |
295 | 0 | apdu.datalen = 2; |
296 | |
|
297 | 0 | r = sc_transmit_apdu(card, &apdu); |
298 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
299 | | |
300 | 0 | if (apdu.p2 == 0x00 && apdu.sw1 == 0x62 && apdu.sw2 == 0x84 ) { |
301 | | /* no FCI => we have a DF (see comment in process_fci()) */ |
302 | 0 | bIsDF = 1; |
303 | 0 | apdu.p2 = 0x0C; |
304 | 0 | apdu.cse = SC_APDU_CASE_3_SHORT; |
305 | 0 | apdu.resplen = 0; |
306 | 0 | apdu.le = 0; |
307 | 0 | r = sc_transmit_apdu(card, &apdu); |
308 | 0 | LOG_TEST_RET(card->ctx, r, "APDU re-transmit failed"); |
309 | 0 | } else if (apdu.sw1 == 0x61 || (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)) { |
310 | | /* SELECT returned some data (possible FCI) => |
311 | | * try a READ BINARY to see if a EF is selected */ |
312 | 0 | sc_apdu_t apdu2; |
313 | 0 | u8 resp2[2]; |
314 | 0 | sc_format_apdu(card, &apdu2, SC_APDU_CASE_2_SHORT, 0xB0, 0, 0); |
315 | 0 | apdu2.resp = (u8*)resp2; |
316 | 0 | apdu2.resplen = 2; |
317 | 0 | apdu2.le = 1; |
318 | 0 | apdu2.lc = 0; |
319 | 0 | r = sc_transmit_apdu(card, &apdu2); |
320 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
321 | 0 | if (apdu2.sw1 == 0x69 && apdu2.sw2 == 0x86) |
322 | | /* no current EF is selected => we have a DF */ |
323 | 0 | bIsDF = 1; |
324 | 0 | } |
325 | | |
326 | 0 | if (apdu.sw1 != 0x61 && (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)) |
327 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); |
328 | | |
329 | 0 | if (file_out) { |
330 | 0 | sc_file_t *file = sc_file_new(); |
331 | 0 | if (!file) |
332 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); |
333 | 0 | file->id = (id_hi << 8) + id_lo; |
334 | |
|
335 | 0 | if (bIsDF) { |
336 | | /* we have a DF */ |
337 | 0 | file->type = SC_FILE_TYPE_DF; |
338 | 0 | file->ef_structure = SC_FILE_EF_UNKNOWN; |
339 | 0 | file->size = 0; |
340 | 0 | file->namelen = 0; |
341 | 0 | file->magic = SC_FILE_MAGIC; |
342 | 0 | *file_out = file; |
343 | 0 | } else { |
344 | | /* ok, assume we have a EF */ |
345 | 0 | r = process_fci(card->ctx, file, apdu.resp, |
346 | 0 | apdu.resplen); |
347 | 0 | if (r != SC_SUCCESS) { |
348 | 0 | sc_file_free(file); |
349 | 0 | return r; |
350 | 0 | } |
351 | | |
352 | 0 | *file_out = file; |
353 | 0 | } |
354 | 0 | } |
355 | | |
356 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); |
357 | 0 | } |
358 | | |
359 | | /*****************************************************************************/ |
360 | | |
361 | | static int atrust_acos_select_file(struct sc_card *card, |
362 | | const struct sc_path *in_path, |
363 | | struct sc_file **file_out) |
364 | 0 | { |
365 | 0 | u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; |
366 | 0 | int r; |
367 | 0 | size_t i, pathlen; |
368 | |
|
369 | 0 | memcpy(path, in_path->value, in_path->len); |
370 | 0 | pathlen = in_path->len; |
371 | |
|
372 | 0 | if (in_path->type == SC_PATH_TYPE_FILE_ID) { |
373 | | /* SELECT EF/DF with ID */ |
374 | | /* Select with 2byte File-ID */ |
375 | 0 | if (pathlen != 2) |
376 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_INVALID_ARGUMENTS); |
377 | 0 | return atrust_acos_select_fid(card, path[0], path[1], file_out); |
378 | 0 | } else if (in_path->type == SC_PATH_TYPE_DF_NAME) { |
379 | | /* SELECT DF with AID */ |
380 | | /* Select with 1-16byte Application-ID */ |
381 | 0 | return atrust_acos_select_aid(card, pathbuf, pathlen, file_out); |
382 | 0 | } else if (in_path->type == SC_PATH_TYPE_PATH) { |
383 | 0 | u8 n_pathbuf[SC_MAX_PATH_SIZE]; |
384 | | |
385 | | /* Select with path (sequence of File-IDs) */ |
386 | | /* ACOS only supports one |
387 | | * level of subdirectories, therefore a path is |
388 | | * at most 3 FID long (the last one being the FID |
389 | | * of a EF) => pathlen must be even and less than 6 |
390 | | */ |
391 | 0 | if (pathlen%2 != 0 || pathlen > 6 || pathlen <= 0) |
392 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); |
393 | | /* if pathlen == 6 then the first FID must be MF (== 3F00) */ |
394 | 0 | if (pathlen == 6 && ( path[0] != 0x3f || path[1] != 0x00 )) |
395 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); |
396 | | |
397 | | /* unify path (the first FID should be MF) */ |
398 | 0 | if (path[0] != 0x3f || path[1] != 0x00) |
399 | 0 | { |
400 | 0 | n_pathbuf[0] = 0x3f; |
401 | 0 | n_pathbuf[1] = 0x00; |
402 | 0 | memcpy(n_pathbuf+2, path, pathlen); |
403 | 0 | path = n_pathbuf; |
404 | 0 | pathlen += 2; |
405 | 0 | } |
406 | |
|
407 | 0 | for (i = 0; i < pathlen - 2; i += 2) { |
408 | 0 | r = atrust_acos_select_fid(card, path[i], path[i + 1], NULL); |
409 | 0 | LOG_TEST_RET(card->ctx, r, "SELECT FILE (DF-ID) failed"); |
410 | 0 | } |
411 | 0 | return atrust_acos_select_fid(card, path[pathlen - 2], path[pathlen - 1], file_out); |
412 | 0 | } else |
413 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); |
414 | 0 | } |
415 | | |
416 | | /** atrust_acos_set_security_env |
417 | | * sets the security environment |
418 | | * \param card pointer to the sc_card object |
419 | | * \param env pointer to a sc_security_env object |
420 | | * \param se_num not used here |
421 | | * \return SC_SUCCESS on success or an error code |
422 | | * |
423 | | * This function sets the security environment (using the |
424 | | * command MANAGE SECURITY ENVIRONMENT). In case a COMPUTE SIGNATURE |
425 | | * operation is requested , this function tries to detect whether |
426 | | * COMPUTE SIGNATURE or INTERNAL AUTHENTICATE must be used for signature |
427 | | * calculation. |
428 | | */ |
429 | | static int atrust_acos_set_security_env(struct sc_card *card, |
430 | | const struct sc_security_env *env, |
431 | | int se_num) |
432 | 0 | { |
433 | 0 | u8 *p, *pp; |
434 | 0 | int r, operation = env->operation; |
435 | 0 | struct sc_apdu apdu; |
436 | 0 | u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; |
437 | 0 | atrust_acos_ex_data *ex_data = (atrust_acos_ex_data *)card->drv_data; |
438 | |
|
439 | 0 | p = sbuf; |
440 | | |
441 | | /* copy key reference, if present */ |
442 | 0 | if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { |
443 | 0 | if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) |
444 | 0 | *p++ = 0x83; |
445 | 0 | else |
446 | 0 | *p++ = 0x84; |
447 | 0 | *p++ = env->key_ref_len; |
448 | 0 | memcpy(p, env->key_ref, env->key_ref_len); |
449 | 0 | p += env->key_ref_len; |
450 | 0 | } |
451 | 0 | pp = p; |
452 | 0 | if (operation == SC_SEC_OPERATION_DECIPHER){ |
453 | 0 | if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02) { |
454 | 0 | *p++ = 0x80; |
455 | 0 | *p++ = 0x01; |
456 | 0 | *p++ = 0x02; |
457 | 0 | } else |
458 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
459 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x81, |
460 | 0 | 0xb8); |
461 | 0 | apdu.data = sbuf; |
462 | 0 | apdu.datalen = p - sbuf; |
463 | 0 | apdu.lc = p - sbuf; |
464 | 0 | apdu.le = 0; |
465 | 0 | r = sc_transmit_apdu(card, &apdu); |
466 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
467 | 0 | if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) |
468 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); |
469 | 0 | return SC_SUCCESS; |
470 | 0 | } |
471 | | /* try COMPUTE SIGNATURE */ |
472 | 0 | if (operation == SC_SEC_OPERATION_SIGN && ( |
473 | 0 | env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 || |
474 | 0 | env->algorithm_flags & SC_ALGORITHM_RSA_PAD_ISO9796)) { |
475 | 0 | if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { |
476 | 0 | *p++ = 0x80; |
477 | 0 | *p++ = 0x01; |
478 | 0 | *p++ = env->algorithm_ref & 0xFF; |
479 | 0 | } else if (env->flags & SC_SEC_ENV_ALG_PRESENT && |
480 | 0 | env->algorithm == SC_ALGORITHM_RSA) { |
481 | | /* set the method to use based on the algorithm_flags */ |
482 | 0 | *p++ = 0x80; |
483 | 0 | *p++ = 0x01; |
484 | 0 | if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01) { |
485 | 0 | if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) |
486 | 0 | *p++ = 0x12; |
487 | 0 | else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_RIPEMD160) |
488 | 0 | *p++ = 0x22; |
489 | 0 | else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5) |
490 | 0 | *p++ = 0x32; |
491 | 0 | else { |
492 | | /* can't use COMPUTE SIGNATURE => |
493 | | * try INTERNAL AUTHENTICATE */ |
494 | 0 | p = pp; |
495 | 0 | operation = SC_SEC_OPERATION_AUTHENTICATE; |
496 | 0 | goto try_authenticate; |
497 | 0 | } |
498 | 0 | } else if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_ISO9796) { |
499 | 0 | if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) |
500 | 0 | *p++ = 0x11; |
501 | 0 | else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_RIPEMD160) |
502 | 0 | *p++ = 0x21; |
503 | 0 | else |
504 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
505 | 0 | } else |
506 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
507 | 0 | } |
508 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xb6); |
509 | 0 | apdu.data = sbuf; |
510 | 0 | apdu.datalen = p - sbuf; |
511 | 0 | apdu.lc = p - sbuf; |
512 | 0 | apdu.le = 0; |
513 | | /* we don't know whether to use |
514 | | * COMPUTE SIGNATURE or INTERNAL AUTHENTICATE */ |
515 | 0 | r = sc_transmit_apdu(card, &apdu); |
516 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
517 | 0 | if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { |
518 | 0 | ex_data->fix_digestInfo = 0; |
519 | 0 | ex_data->sec_ops = SC_SEC_OPERATION_SIGN; |
520 | 0 | return SC_SUCCESS; |
521 | 0 | } |
522 | | /* reset pointer */ |
523 | 0 | p = pp; |
524 | | /* doesn't work => try next op */ |
525 | 0 | operation = SC_SEC_OPERATION_AUTHENTICATE; |
526 | 0 | } |
527 | 0 | try_authenticate: |
528 | | /* try INTERNAL AUTHENTICATE */ |
529 | 0 | if (operation == SC_SEC_OPERATION_AUTHENTICATE && |
530 | 0 | env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { |
531 | 0 | *p++ = 0x80; |
532 | 0 | *p++ = 0x01; |
533 | 0 | *p++ = 0x01; |
534 | |
|
535 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xa4); |
536 | 0 | apdu.data = sbuf; |
537 | 0 | apdu.datalen = p - sbuf; |
538 | 0 | apdu.lc = p - sbuf; |
539 | 0 | apdu.le = 0; |
540 | 0 | r = sc_transmit_apdu(card, &apdu); |
541 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
542 | 0 | if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) |
543 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); |
544 | 0 | ex_data->fix_digestInfo = env->algorithm_flags; |
545 | 0 | ex_data->sec_ops = SC_SEC_OPERATION_AUTHENTICATE; |
546 | 0 | return SC_SUCCESS; |
547 | 0 | } |
548 | | |
549 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
550 | 0 | } |
551 | | |
552 | | /*****************************************************************************/ |
553 | | |
554 | | static int atrust_acos_compute_signature(struct sc_card *card, |
555 | | const u8 * data, size_t datalen, |
556 | | u8 * out, size_t outlen) |
557 | 0 | { |
558 | 0 | int r; |
559 | 0 | struct sc_apdu apdu; |
560 | 0 | u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; |
561 | 0 | u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; |
562 | 0 | atrust_acos_ex_data *ex_data = (atrust_acos_ex_data *)card->drv_data; |
563 | |
|
564 | 0 | if (datalen > SC_MAX_APDU_BUFFER_SIZE) |
565 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); |
566 | | |
567 | 0 | if (ex_data->sec_ops == SC_SEC_OPERATION_SIGN) { |
568 | | /* compute signature with the COMPUTE SIGNATURE command */ |
569 | | |
570 | | /* set the hash value */ |
571 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, |
572 | 0 | 0x90, 0x81); |
573 | 0 | apdu.resp = rbuf; |
574 | 0 | apdu.resplen = sizeof(rbuf); |
575 | 0 | apdu.le = 0; |
576 | 0 | memcpy(sbuf, data, datalen); |
577 | 0 | apdu.data = sbuf; |
578 | 0 | apdu.lc = datalen; |
579 | 0 | apdu.datalen = datalen; |
580 | 0 | r = sc_transmit_apdu(card, &apdu); |
581 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
582 | 0 | if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) |
583 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, |
584 | 0 | sc_check_sw(card, apdu.sw1, apdu.sw2)); |
585 | | |
586 | | /* call COMPUTE SIGNATURE */ |
587 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x2A, |
588 | 0 | 0x9E, 0x9A); |
589 | 0 | apdu.resp = rbuf; |
590 | 0 | apdu.resplen = sizeof(rbuf); |
591 | 0 | apdu.le = 256; |
592 | |
|
593 | 0 | apdu.lc = 0; |
594 | 0 | apdu.datalen = 0; |
595 | 0 | r = sc_transmit_apdu(card, &apdu); |
596 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
597 | 0 | if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { |
598 | 0 | size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; |
599 | 0 | memcpy(out, apdu.resp, len); |
600 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len); |
601 | 0 | } |
602 | 0 | } else if (ex_data->sec_ops == SC_SEC_OPERATION_AUTHENTICATE) { |
603 | 0 | size_t tmp_len; |
604 | | /* call INTERNAL AUTHENTICATE */ |
605 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0x10, 0x00); |
606 | | /* fix/create DigestInfo structure (if necessary) */ |
607 | 0 | if (ex_data->fix_digestInfo) { |
608 | 0 | unsigned int flags = ex_data->fix_digestInfo & SC_ALGORITHM_RSA_HASHES; |
609 | 0 | if (flags == 0x0) |
610 | | /* XXX: assume no hash is wanted */ |
611 | 0 | flags = SC_ALGORITHM_RSA_HASH_NONE; |
612 | 0 | tmp_len = sizeof(sbuf); |
613 | 0 | r = sc_pkcs1_encode(card->ctx, flags, data, datalen, |
614 | 0 | sbuf, &tmp_len, sizeof(sbuf)*8, NULL); |
615 | 0 | if (r < 0) |
616 | 0 | return r; |
617 | 0 | } else { |
618 | 0 | memcpy(sbuf, data, datalen); |
619 | 0 | tmp_len = datalen; |
620 | 0 | } |
621 | 0 | apdu.lc = tmp_len; |
622 | 0 | apdu.data = sbuf; |
623 | 0 | apdu.datalen = tmp_len; |
624 | 0 | apdu.resp = rbuf; |
625 | 0 | apdu.resplen = sizeof(rbuf); |
626 | 0 | apdu.le = 256; |
627 | 0 | r = sc_transmit_apdu(card, &apdu); |
628 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
629 | 0 | if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) |
630 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); |
631 | 0 | { |
632 | 0 | size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; |
633 | |
|
634 | 0 | memcpy(out, apdu.resp, len); |
635 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len); |
636 | 0 | } |
637 | 0 | } else |
638 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); |
639 | | |
640 | | /* clear old state */ |
641 | 0 | ex_data->sec_ops = 0; |
642 | 0 | ex_data->fix_digestInfo = 0; |
643 | |
|
644 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); |
645 | 0 | } |
646 | | |
647 | | /*****************************************************************************/ |
648 | | |
649 | | static int atrust_acos_decipher(struct sc_card *card, |
650 | | const u8 * crgram, size_t crgram_len, |
651 | | u8 * out, size_t outlen) |
652 | 0 | { |
653 | 0 | int r; |
654 | 0 | struct sc_apdu apdu; |
655 | 0 | u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; |
656 | 0 | u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; |
657 | |
|
658 | 0 | assert(card != NULL && crgram != NULL && out != NULL); |
659 | 0 | LOG_FUNC_CALLED(card->ctx); |
660 | 0 | if (crgram_len > 255) |
661 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); |
662 | | |
663 | | /* INS: 0x2A PERFORM SECURITY OPERATION |
664 | | * P1: 0x80 Resp: Plain value |
665 | | * P2: 0x86 Cmd: Padding indicator byte followed by cryptogram */ |
666 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); |
667 | 0 | apdu.resp = rbuf; |
668 | 0 | apdu.resplen = sizeof(rbuf); |
669 | |
|
670 | 0 | sbuf[0] = 0; /* padding indicator byte, 0x00 = No further indication */ |
671 | 0 | memcpy(sbuf + 1, crgram, crgram_len); |
672 | 0 | apdu.data = sbuf; |
673 | 0 | apdu.lc = crgram_len + 1; |
674 | 0 | apdu.datalen = crgram_len + 1; |
675 | 0 | apdu.le = 256; |
676 | 0 | r = sc_transmit_apdu(card, &apdu); |
677 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
678 | 0 | if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { |
679 | 0 | size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; |
680 | |
|
681 | 0 | memcpy(out, apdu.resp, len); |
682 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len); |
683 | 0 | } |
684 | | |
685 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); |
686 | 0 | } |
687 | | |
688 | | /*****************************************************************************/ |
689 | | |
690 | | static int atrust_acos_check_sw(struct sc_card *card, unsigned int sw1, |
691 | | unsigned int sw2) |
692 | 0 | { |
693 | |
|
694 | 0 | sc_log(card->ctx, "sw1 = 0x%02x, sw2 = 0x%02x\n", sw1, sw2); |
695 | |
|
696 | 0 | if (sw1 == 0x90 && sw2 == 0x00) |
697 | 0 | return SC_SUCCESS; |
698 | 0 | if (sw1 == 0x63 && (sw2 & ~0x0fU) == 0xc0 ) |
699 | 0 | { |
700 | 0 | sc_log(card->ctx, "Verification failed (remaining tries: %d)\n", |
701 | 0 | (sw2 & 0x0f)); |
702 | 0 | return SC_ERROR_PIN_CODE_INCORRECT; |
703 | 0 | } |
704 | | |
705 | | /* iso error */ |
706 | 0 | return iso_ops->check_sw(card, sw1, sw2); |
707 | 0 | } |
708 | | |
709 | | /*****************************************************************************/ |
710 | | |
711 | | static int acos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) |
712 | 0 | { |
713 | 0 | int r; |
714 | 0 | u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; |
715 | 0 | sc_apdu_t apdu; |
716 | |
|
717 | 0 | if (!serial) |
718 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
719 | | /* see if we have cached serial number */ |
720 | 0 | if (card->serialnr.len) { |
721 | 0 | memcpy(serial, &card->serialnr, sizeof(*serial)); |
722 | 0 | return SC_SUCCESS; |
723 | 0 | } |
724 | | /* get serial number via GET CARD DATA */ |
725 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xf6, 0x00, 0x00); |
726 | 0 | apdu.cla |= 0x80; |
727 | 0 | apdu.resp = rbuf; |
728 | 0 | apdu.resplen = sizeof(rbuf); |
729 | 0 | apdu.le = 256; |
730 | 0 | apdu.lc = 0; |
731 | 0 | apdu.datalen = 0; |
732 | 0 | r = sc_transmit_apdu(card, &apdu); |
733 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
734 | 0 | if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) |
735 | 0 | return SC_ERROR_INTERNAL; |
736 | | /* cache serial number */ |
737 | 0 | memcpy(card->serialnr.value, apdu.resp, MIN(apdu.resplen, SC_MAX_SERIALNR)); |
738 | 0 | card->serialnr.len = MIN(apdu.resplen, SC_MAX_SERIALNR); |
739 | | /* copy and return serial number */ |
740 | 0 | memcpy(serial, &card->serialnr, sizeof(*serial)); |
741 | 0 | return SC_SUCCESS; |
742 | 0 | } |
743 | | |
744 | | /*****************************************************************************/ |
745 | | |
746 | | static int atrust_acos_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr) |
747 | 0 | { |
748 | |
|
749 | 0 | switch (cmd) |
750 | 0 | { |
751 | 0 | case SC_CARDCTL_GET_SERIALNR: |
752 | 0 | return acos_get_serialnr(card, (sc_serial_number_t *)ptr); |
753 | 0 | default: |
754 | 0 | return SC_ERROR_NOT_SUPPORTED; |
755 | 0 | } |
756 | 0 | } |
757 | | |
758 | | /*****************************************************************************/ |
759 | | |
760 | | static int atrust_acos_logout(struct sc_card *card) |
761 | 0 | { |
762 | 0 | int r; |
763 | 0 | struct sc_apdu apdu; |
764 | 0 | const u8 mf_buf[2] = {0x3f, 0x00}; |
765 | |
|
766 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x00, 0x0C); |
767 | 0 | apdu.le = 0; |
768 | 0 | apdu.lc = 2; |
769 | 0 | apdu.data = mf_buf; |
770 | 0 | apdu.datalen = 2; |
771 | 0 | apdu.resplen = 0; |
772 | |
|
773 | 0 | r = sc_transmit_apdu(card, &apdu); |
774 | 0 | LOG_TEST_RET(card->ctx, r, "APDU re-transmit failed"); |
775 | | |
776 | 0 | if (apdu.sw1 == 0x69 && apdu.sw2 == 0x85) |
777 | | /* the only possible reason for this error here is, afaik, |
778 | | * that no MF exists, but then there's no need to logout |
779 | | * => return SC_SUCCESS |
780 | | */ |
781 | 0 | return SC_SUCCESS; |
782 | 0 | return sc_check_sw(card, apdu.sw1, apdu.sw2); |
783 | 0 | } |
784 | | |
785 | | /*****************************************************************************/ |
786 | | |
787 | | static struct sc_card_driver * sc_get_driver(void) |
788 | 0 | { |
789 | 0 | struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); |
790 | 0 | if (iso_ops == NULL) |
791 | 0 | iso_ops = iso_drv->ops; |
792 | |
|
793 | 0 | atrust_acos_ops = *iso_drv->ops; |
794 | 0 | atrust_acos_ops.match_card = atrust_acos_match_card; |
795 | 0 | atrust_acos_ops.init = atrust_acos_init; |
796 | 0 | atrust_acos_ops.finish = atrust_acos_finish; |
797 | 0 | atrust_acos_ops.select_file = atrust_acos_select_file; |
798 | 0 | atrust_acos_ops.check_sw = atrust_acos_check_sw; |
799 | 0 | atrust_acos_ops.create_file = NULL; |
800 | 0 | atrust_acos_ops.delete_file = NULL; |
801 | 0 | atrust_acos_ops.set_security_env = atrust_acos_set_security_env; |
802 | 0 | atrust_acos_ops.compute_signature = atrust_acos_compute_signature; |
803 | 0 | atrust_acos_ops.decipher = atrust_acos_decipher; |
804 | 0 | atrust_acos_ops.card_ctl = atrust_acos_card_ctl; |
805 | 0 | atrust_acos_ops.logout = atrust_acos_logout; |
806 | |
|
807 | 0 | return &atrust_acos_drv; |
808 | 0 | } |
809 | | |
810 | | /*****************************************************************************/ |
811 | | |
812 | | struct sc_card_driver * sc_get_atrust_acos_driver(void) |
813 | 0 | { |
814 | 0 | return sc_get_driver(); |
815 | 0 | } |