root/trunk/src/libopensc/card-flex.c

Revision 3085, 36.8 KB (checked in by aj, 2 years ago)

convert to utf-8.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * card-flex.c: Support for Schlumberger cards
3 *
4 * Copyright (C) 2001, 2002  Juha YrjölÀ <juha.yrjola@iki.fi>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21#include "internal.h"
22#include "cardctl.h"
23#include <stdlib.h>
24#include <string.h>
25
26#define IS_CYBERFLEX(card)      (card->type == SC_CARD_TYPE_FLEX_CYBER)
27
28static struct sc_atr_table flex_atrs[] = {
29        /* Cryptoflex */
30        /* 8k win2000 */
31        { "3b:95:15:40:20:68:01:02:00:00", NULL, "Cryptoflex 8K", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL },
32        /* 8k */
33        { "3B:95:15:40:FF:68:01:02:02:01", NULL, "Cryptoflex 8K", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL },
34        /* 8k */
35        { "3B:95:15:40:FF:68:01:02:02:04", NULL, "Cryptoflex 8K", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL },
36        /* 8k */
37        { "3B:85:40:20:68:01:01:05:01", NULL, "Cryptoflex 8K", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL },
38        /* 16k */
39        { "3B:95:94:40:FF:63:01:01:02:01", NULL, "Cryptoflex 16K", SC_CARD_TYPE_FLEX_CRYPTO, SC_CARD_FLAG_ONBOARD_KEY_GEN, NULL },
40        /* "16K+SS1" alias Cryptoflex 16 card with Standard Softmask V1 */
41        /* (taken from Cryptoflex Card Programmers Guide 4.5 Page xviii) */
42        /* last two bytes can be ignored - version of the softmask */
43        { "3B:95:15:40:FF:63:01:01:02:01", "FF:FF:FF:FF:FF:FF:FF:FF:00:00",
44                "Cryptoflex 16K", SC_CARD_TYPE_FLEX_CRYPTO,
45                SC_CARD_FLAG_ONBOARD_KEY_GEN, NULL },
46        /* 32K v4 */
47        /* "32K+SS1" alias Cryptoflex 32 card with Standard Softmask V1 */
48        /* (taken from Cryptoflex Card Programmers Guide 4.5 Page xviii) */
49        /* last two bytes can be ignored - version of the softmask */
50        { "3B:95:18:40:FF:64:02:01:01:02","FF:FF:FF:FF:FF:FF:FF:FF:00:00",
51                "Cryptoflex 32K v4", SC_CARD_TYPE_FLEX_CRYPTO,
52                SC_CARD_FLAG_ONBOARD_KEY_GEN, NULL },
53        /* "32K+e-gate" alias Cryptoflex e-gate 32K card */
54        /* (taken from Cryptoflex Card Programmers Guide 4.5 Page xviii) */
55        /* last two bytes can be ignored - version of the softmask */
56        { "3B:95:18:40:FF:62:01:01:00:00", "FF:FF:FF:FF:FF:FF:FF:FF:00:00",
57                "Cryptoflex e-gate 32K", SC_CARD_TYPE_FLEX_CRYPTO,
58                SC_CARD_FLAG_ONBOARD_KEY_GEN, NULL },
59        /* 32K e-gate */
60        { "3B:95:18:40:FF:62:01:02:01:04", NULL, "Cryptoflex 32K e-gate", SC_CARD_TYPE_FLEX_CRYPTO, SC_CARD_FLAG_ONBOARD_KEY_GEN, NULL },
61        /* 32K e-gate v4 */
62        { "3B:95:18:40:FF:62:04:01:01:05", NULL, "Cryptoflex 32K e-gate v4", SC_CARD_TYPE_FLEX_CRYPTO, SC_CARD_FLAG_ONBOARD_KEY_GEN, NULL },
63
64        /* new cryptoflex 32k card - atr looks very similiar to old 8k card */
65        { "3b:95:15:40:ff:68:01:02:45:47", NULL, "Cryptoflex 32K", SC_CARD_TYPE_FLEX_CRYPTO, SC_CARD_FLAG_ONBOARD_KEY_GEN, NULL },
66
67        { "3B:E2:00:00:40:20:49:06", NULL, "Cryptoflex", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL },
68        /* + full DES option */
69        { "3B:E2:00:00:40:20:49:05", NULL, "Cryptoflex", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL },
70        /* + Key Generation */
71        { "3B:E2:00:00:40:20:49:07", NULL, "Cryptoflex", SC_CARD_TYPE_FLEX_CRYPTO, SC_CARD_FLAG_ONBOARD_KEY_GEN, NULL },
72        /* + Key Generation */
73        { "3B:85:40:20:68:01:01:03:05", NULL, "Cryptoflex", SC_CARD_TYPE_FLEX_CRYPTO, SC_CARD_FLAG_ONBOARD_KEY_GEN, NULL },
74
75        /* Multiflex */
76        /* 3K */
77        { "3B:02:14:50", NULL, "Multiflex 3K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL },
78        /* 4K */
79        { "3B:19:14:55:90:01:02:01:00:05:04:B0", NULL, "Multiflex 4K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL },
80        /* 8K */
81        { "3B:32:15:00:06:80", NULL, "Multiflex 8K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL },
82        /* 8K + full DES option */
83        { "3B:32:15:00:06:95", NULL, "Multiflex 8K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL },
84        /* 8K */
85        { "3B:19:14:59:01:01:0F:01:00:05:08:B0", NULL, "Multiflex 8K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL },
86        /* 8K */
87        { "3B:19:14:55:90:01:01:01:00:05:08:B0", NULL, "Multiflex 8K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL },
88
89        /* Cyberflex Access */
90        /* Crypto */
91        { "3B:16:94:81:10:06:01:81:3F", NULL, "Cyberflex Access", SC_CARD_TYPE_FLEX_CYBER, 0, NULL },
92        /* Aug. Crypto */
93        { "3B:16:94:81:10:06:01:81:2F", NULL, "Cyberflex Access", SC_CARD_TYPE_FLEX_CYBER, 0, NULL },
94        { NULL, NULL, NULL, 0, 0, NULL }
95};
96
97struct flex_private_data {
98        int     rsa_key_ref;
99
100        /* Support card variations without having to
101         * do the if (card->type ...) thing
102         * all the time */
103        u8      aak_key_ref;
104};
105
106#define DRV_DATA(card)  ((struct flex_private_data *) (card)->drv_data)
107
108static struct sc_card_operations cryptoflex_ops;
109static struct sc_card_operations cyberflex_ops;
110static struct sc_card_operations *iso_ops;
111static struct sc_card_driver cryptoflex_drv = {
112        "Schlumberger Multiflex/Cryptoflex",
113        "flex",
114        &cryptoflex_ops,
115        NULL, 0, NULL
116};
117static struct sc_card_driver cyberflex_drv = {
118        "Schlumberger Cyberflex",
119        "cyberflex",
120        &cyberflex_ops,
121        NULL, 0, NULL
122};
123
124static int flex_finish(sc_card_t *card)
125{
126        free(card->drv_data);
127        return 0;
128}
129
130static int cryptoflex_match_card(sc_card_t *card)
131{
132        int i;
133
134        i = _sc_match_atr(card, flex_atrs, NULL);
135        if (i < 0)
136                return 0;
137        switch (flex_atrs[i].type) {
138        case SC_CARD_TYPE_FLEX_CRYPTO:
139        case SC_CARD_TYPE_FLEX_MULTI:
140                card->name = flex_atrs[i].name;
141                card->type = flex_atrs[i].type;
142                card->flags = flex_atrs[i].flags;
143                return 1;
144        }
145        return 0;
146}
147
148static int cyberflex_match_card(sc_card_t *card)
149{
150        int i;
151
152        i = _sc_match_atr(card, flex_atrs, NULL);
153        if (i < 0)
154                return 0;
155        switch (flex_atrs[i].type) {
156        case SC_CARD_TYPE_FLEX_CYBER:
157                card->name = flex_atrs[i].name;
158                card->type = flex_atrs[i].type;
159                card->flags = flex_atrs[i].flags;
160                return 1;
161        }
162        return 0;
163}
164
165static int flex_init(sc_card_t *card)
166{
167        struct flex_private_data *data;
168
169        if (!(data = (struct flex_private_data *) malloc(sizeof(*data))))
170                return SC_ERROR_OUT_OF_MEMORY;
171        card->drv_data = data;
172
173        card->cla = 0xC0;
174        data->aak_key_ref = 1;
175
176        /* Override Cryptoflex defaults for specific card types */
177        switch (card->type) {
178        case SC_CARD_TYPE_FLEX_CYBER:
179                card->cla = 0x00;
180                data->aak_key_ref = 0;
181                break;
182        }
183
184        /* FIXME: Card type detection */
185        if (1) {
186                unsigned long flags;
187               
188                flags = SC_ALGORITHM_RSA_RAW;
189                flags |= SC_ALGORITHM_RSA_HASH_NONE;
190                if (card->flags & SC_CARD_FLAG_ONBOARD_KEY_GEN)
191                        flags |= SC_ALGORITHM_ONBOARD_KEY_GEN;
192
193                _sc_card_add_rsa_alg(card, 512, flags, 0);
194                _sc_card_add_rsa_alg(card, 768, flags, 0);
195                _sc_card_add_rsa_alg(card, 1024, flags, 0);
196                _sc_card_add_rsa_alg(card, 2048, flags, 0);
197        }
198
199        /* SCardTransmit failed: 8010002f
200         * this can be solved with a small delay. */
201        msleep(100);
202
203        /* State that we have an RNG */
204        card->caps |= SC_CARD_CAP_RNG;
205
206        return 0;
207}
208
209static void
210add_acl_entry(sc_card_t *card, sc_file_t *file, unsigned int op, u8 nibble)
211{
212        struct flex_private_data *prv = DRV_DATA(card);
213
214        switch (nibble) {
215        case 0:
216                sc_file_add_acl_entry(file, op, SC_AC_NONE, SC_AC_KEY_REF_NONE);
217                break;
218        case 1:
219                sc_file_add_acl_entry(file, op, SC_AC_CHV, 1);
220                break;
221        case 2:
222                sc_file_add_acl_entry(file, op, SC_AC_CHV, 2);
223                break;
224        case 3:
225                sc_file_add_acl_entry(file, op, SC_AC_PRO, SC_AC_KEY_REF_NONE);
226                break;
227        case 4:
228                /* Assume the key is the AAK */
229                sc_file_add_acl_entry(file, op, SC_AC_AUT, prv->aak_key_ref);
230                break;
231        case 6:
232                sc_file_add_acl_entry(file, op, SC_AC_CHV, 1);
233                sc_file_add_acl_entry(file, op, SC_AC_PRO, SC_AC_KEY_REF_NONE);
234                break;
235        case 7:
236                sc_file_add_acl_entry(file, op, SC_AC_CHV, 2);
237                sc_file_add_acl_entry(file, op, SC_AC_PRO, SC_AC_KEY_REF_NONE);
238                break;
239        case 8:
240                sc_file_add_acl_entry(file, op, SC_AC_CHV, 1);
241                /* Assume the key is the AAK */
242                sc_file_add_acl_entry(file, op, SC_AC_AUT, prv->aak_key_ref);
243                break;
244        case 9:
245                sc_file_add_acl_entry(file, op, SC_AC_CHV, 2);
246                /* Assume the key is the AAK */
247                sc_file_add_acl_entry(file, op, SC_AC_AUT, prv->aak_key_ref);
248                break;
249        case 15:
250                sc_file_add_acl_entry(file, op, SC_AC_NEVER, SC_AC_KEY_REF_NONE);
251                break;
252        default:
253                sc_file_add_acl_entry(file, op, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE);
254                break;
255        }
256}
257
258static int
259cryptoflex_get_ac_keys(sc_card_t *card, sc_file_t *file)
260{
261#if 0
262        sc_apdu_t apdu;
263        u8 rbuf[3];
264        int r;
265       
266        sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xC4, 0x00, 0x00);
267        apdu.cla = 0xF0 /* 0x00 for Cyberflex */;
268        apdu.le = 3;
269        apdu.resplen = 3;
270        apdu.resp = rbuf;
271        r = sc_transmit_apdu(card, &apdu);
272        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
273        if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00)
274                return 0;
275        sc_debug(card->ctx, "AC Keys: %02X %02X %02X\n", rbuf[0], rbuf[1], rbuf[2]);
276#endif
277        return 0;
278}
279
280static int
281cryptoflex_process_file_attrs(sc_card_t *card, sc_file_t *file,
282                        const u8 *buf, size_t buflen)
283{
284        sc_context_t *ctx = card->ctx;
285        const u8 *p = buf + 2;
286        u8 b1, b2;
287        int left, is_mf = 0;
288       
289        if (buflen < 14)
290                return -1;
291        b1 = *p++;
292        b2 = *p++;
293        file->size = (b1 << 8) + b2;
294        b1 = *p++;
295        b2 = *p++;
296        file->id = (b1 << 8) + b2;
297        if (file->id == 0x3F00)
298                is_mf = 1;
299        switch (*p) {
300        case 0x01:
301                file->type = SC_FILE_TYPE_WORKING_EF;
302                file->ef_structure = SC_FILE_EF_TRANSPARENT;
303                break;
304        case 0x02:
305                file->type = SC_FILE_TYPE_WORKING_EF;
306                file->ef_structure = SC_FILE_EF_LINEAR_FIXED;
307                break;
308        case 0x04:
309                file->type = SC_FILE_TYPE_WORKING_EF;
310                file->ef_structure = SC_FILE_EF_LINEAR_VARIABLE;
311                break;
312        case 0x06:
313                file->type = SC_FILE_TYPE_WORKING_EF;
314                file->ef_structure = SC_FILE_EF_CYCLIC;
315                break;
316        case 0x38:
317                file->type = SC_FILE_TYPE_DF;
318                break;
319        default:
320                sc_error(ctx, "invalid file type: 0x%02X\n", *p);
321                return SC_ERROR_UNKNOWN_DATA_RECEIVED;
322        }
323        p += 2;
324        if (file->type == SC_FILE_TYPE_DF) {
325                add_acl_entry(card, file, SC_AC_OP_LIST_FILES, (u8)(p[0] >> 4));
326                add_acl_entry(card, file, SC_AC_OP_DELETE, (u8)(p[1] >> 4));
327                add_acl_entry(card, file, SC_AC_OP_CREATE, (u8)(p[1] & 0x0F));
328        } else { /* EF */
329                add_acl_entry(card, file, SC_AC_OP_READ, (u8)(p[0] >> 4));
330                switch (file->ef_structure) {
331                case SC_FILE_EF_TRANSPARENT:
332                        add_acl_entry(card, file, SC_AC_OP_UPDATE, (u8)(p[0] & 0x0F));
333                        break;
334                case SC_FILE_EF_LINEAR_FIXED:
335                case SC_FILE_EF_LINEAR_VARIABLE:
336                        add_acl_entry(card, file, SC_AC_OP_UPDATE, (u8)(p[0] & 0x0F));
337                        break;
338                case SC_FILE_EF_CYCLIC:
339#if 0
340                        /* FIXME */
341                        file->acl[SC_AC_OP_DECREASE] = ac_to_acl(p[0] & 0x0F);
342#endif
343                        break;
344                }
345        }
346        if (file->type != SC_FILE_TYPE_DF || is_mf) {
347                add_acl_entry(card, file, SC_AC_OP_REHABILITATE, (u8)(p[2] >> 4));
348                add_acl_entry(card, file, SC_AC_OP_INVALIDATE, (u8)(p[2] & 0x0F));
349        }
350        p += 3;
351        if (*p++)
352                file->status = SC_FILE_STATUS_ACTIVATED;
353        else
354                file->status = SC_FILE_STATUS_INVALIDATED;
355        left = *p++;
356
357        return cryptoflex_get_ac_keys(card, file);
358}
359
360static int
361cyberflex_process_file_attrs(sc_card_t *card, sc_file_t *file,
362                        const u8 *buf, size_t buflen)
363{
364        sc_context_t *ctx = card->ctx;
365        const u8 *p = buf + 2;
366        const u8 *pos;
367        u8 b1, b2;
368        int is_mf = 0;
369
370        if (buflen < 14)
371                return -1;
372        b1 = *p++;
373        b2 = *p++;
374        file->size = (b1 << 8) + b2;
375        b1 = *p++;
376        b2 = *p++;
377        file->id = (b1 << 8) + b2;
378        switch (*p) {
379        case 0x01:
380                is_mf = 1;
381                break;
382        case 0x02:
383                file->type = SC_FILE_TYPE_DF;
384                break;
385        case 0x04:
386                file->type = SC_FILE_TYPE_WORKING_EF;
387                break;
388        default:
389                sc_error(ctx, "invalid file type: 0x%02X\n", *p);
390                return SC_ERROR_UNKNOWN_DATA_RECEIVED;
391        }
392
393        if (is_mf) {
394                sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, SC_AC_AUT, 0);
395                sc_file_add_acl_entry(file, SC_AC_OP_DELETE, SC_AC_AUT, 0);
396                sc_file_add_acl_entry(file, SC_AC_OP_CREATE, SC_AC_AUT, 0);
397        } else {
398                p += 2;
399                if (file->type == SC_FILE_TYPE_DF) {
400                        add_acl_entry(card, file, SC_AC_OP_LIST_FILES, (u8)(p[0] >> 4));
401                        add_acl_entry(card, file, SC_AC_OP_DELETE, (u8)(p[1] >> 4));
402                        add_acl_entry(card, file, SC_AC_OP_CREATE, (u8)(p[1] & 0x0F));
403                } else { /* EF */
404                        add_acl_entry(card, file, SC_AC_OP_READ, (u8)(p[0] >> 4));
405                }
406        }
407        if (file->type != SC_FILE_TYPE_DF) {
408                add_acl_entry(card, file, SC_AC_OP_REHABILITATE, (u8)(p[2] >> 4));
409                add_acl_entry(card, file, SC_AC_OP_INVALIDATE, (u8)(p[2] & 0x0F));
410        }
411        pos = p;
412        p += 3;
413        if (*p++)
414                file->status = SC_FILE_STATUS_ACTIVATED;
415        else
416                file->status = SC_FILE_STATUS_INVALIDATED;
417        p++;
418        if (0 == is_mf) {
419                p++;
420                switch (*p) {
421                case  0x00:
422                        file->ef_structure = SC_FILE_EF_TRANSPARENT;
423                        break;
424                case  0x01:
425                        file->ef_structure = SC_FILE_EF_LINEAR_FIXED;
426                        break;
427                case  0x02:
428                        file->ef_structure = SC_FILE_EF_LINEAR_VARIABLE;
429                        break;
430                case  0x03:
431                        file->ef_structure = SC_FILE_EF_CYCLIC;
432                        break;
433                case  0x04:
434#if 0
435                        file->ef_structure = SC_FILE_EF_PROGRAM;
436#endif
437                        break;
438                default:
439                        sc_error(ctx, "invalid file type: 0x%02X\n", *p);
440                        return SC_ERROR_UNKNOWN_DATA_RECEIVED;
441                }
442                switch (file->ef_structure) {
443                case SC_FILE_EF_TRANSPARENT:
444                        add_acl_entry(card, file, SC_AC_OP_UPDATE, (u8)(pos[0] & 0x0F));
445                        break;
446                case SC_FILE_EF_LINEAR_FIXED:
447                case SC_FILE_EF_LINEAR_VARIABLE:
448                        add_acl_entry(card, file, SC_AC_OP_UPDATE, (u8)(pos[0] & 0x0F));
449                        break;
450                case SC_FILE_EF_CYCLIC:
451#if 0
452                        /* FIXME */
453                        file->acl[SC_AC_OP_DECREASE] = ac_to_acl(pos[0] & 0x0F);
454#endif
455                        break;
456                }
457        }
458        return 0;
459}
460
461static int check_path(sc_card_t *card, const u8 **pathptr, size_t *pathlen,
462                      int need_info)
463{
464        const u8 *curptr = card->cache.current_path.value;
465        const u8 *ptr = *pathptr;
466        size_t curlen = card->cache.current_path.len;
467        size_t len = *pathlen;
468
469        if (curlen < 2)
470                return 0;
471        if (len < 2)
472                return 0;
473        if (memcmp(ptr, "\x3F\x00", 2) != 0) {
474                /* Skip the MF id */
475                curptr += 2;
476                curlen -= 2;
477        }
478        if (len == curlen && memcmp(ptr, curptr, len) == 0) {
479                if (need_info)
480                        return 0;
481                *pathptr = ptr + len;
482                *pathlen = 0;
483                return 1;
484        }
485        if (curlen < len && memcmp(ptr, curptr, curlen) == 0) {
486                *pathptr = ptr + curlen;
487                *pathlen = len - curlen;
488                return 1;
489        }
490        /* FIXME: Build additional logic */
491        return 0;
492}
493
494static void cache_path(sc_card_t *card, const sc_path_t *path,
495        int result)
496{
497        sc_path_t *curpath = &card->cache.current_path;
498       
499        if (result < 0) {
500                curpath->len = 0;
501                return;
502        }
503
504        switch (path->type) {
505        case SC_PATH_TYPE_FILE_ID:
506                if (path->value[0] == 0x3F && path->value[1] == 0x00)
507                        sc_format_path("3F00", curpath);
508                else {
509                        if (curpath->len + 2 > SC_MAX_PATH_SIZE) {
510                                curpath->len = 0;
511                                return;
512                        }
513                        memcpy(curpath->value + curpath->len, path->value, 2);
514                        curpath->len += 2;
515                }
516                break;
517        case SC_PATH_TYPE_PATH:
518                curpath->len = 0;
519                if (path->value[0] != 0x3F || path->value[1] != 0)
520                        sc_format_path("3F00", curpath);
521                if (curpath->len + path->len > SC_MAX_PATH_SIZE) {
522                        curpath->len = 0;
523                        return;
524                }
525                memcpy(curpath->value + curpath->len, path->value, path->len);
526                curpath->len += path->len;
527                break;
528        case SC_PATH_TYPE_DF_NAME:
529                /* All bets are off */
530                curpath->len = 0;
531                break;
532        }
533}
534
535static int select_file_id(sc_card_t *card, const u8 *buf, size_t buflen,
536                          u8 p1, sc_file_t **file_out)
537{
538        int r;
539        sc_apdu_t apdu;
540        u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
541        sc_file_t *file;
542
543        if (card->ctx->debug >= 4) {
544                char    string[32];
545
546                sc_bin_to_hex(buf, buflen, string, sizeof(string), 0);
547                sc_debug(card->ctx, "called, p1=%u, path=%s\n", p1, string);
548        }
549
550        sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, p1, 0);
551        apdu.resp = rbuf;
552        apdu.resplen = sizeof(rbuf);
553        apdu.datalen = buflen;
554        apdu.data = buf;
555        apdu.lc = buflen;
556        apdu.le = 252;
557
558        /* No need to get file information, if file is NULL. */
559        if (file_out == NULL) {
560                apdu.cse = SC_APDU_CASE_3_SHORT;
561                apdu.le = 0;
562        }
563        r = sc_transmit_apdu(card, &apdu);
564        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
565        r = sc_check_sw(card, apdu.sw1, apdu.sw2);
566        SC_TEST_RET(card->ctx, r, "Card returned error");
567
568        if (file_out == NULL)
569                return 0;
570
571        if (apdu.resplen < 14)
572                return SC_ERROR_UNKNOWN_DATA_RECEIVED;
573        if (apdu.resp[0] == 0x6F) {
574                sc_error(card->ctx, "unsupported: card returned FCI\n");
575                return SC_ERROR_UNKNOWN_DATA_RECEIVED; /* FIXME */
576        }
577        file = sc_file_new();
578        if (file == NULL)
579                SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY);
580
581        /* We abuse process_fci here even though it's not the real FCI. */
582        r = card->ops->process_fci(card, file, apdu.resp, apdu.resplen);
583        if (r) {
584                sc_file_free(file);
585                return r;
586        }
587
588        *file_out = file;
589        return 0;
590}
591
592static int flex_select_file(sc_card_t *card, const sc_path_t *path,
593                             sc_file_t **file_out)
594{
595        int r;
596        const u8 *pathptr = path->value;
597        size_t pathlen = path->len;
598        int locked = 0, magic_done;
599        u8 p1 = 0;
600
601        if (card->ctx->debug >= 2) {
602                char pbuf[SC_MAX_PATH_STRING_SIZE];
603
604                r = sc_path_print(pbuf, sizeof(pbuf), &card->cache.current_path);
605                if (r != SC_SUCCESS)
606                        pbuf[0] = '\0';
607
608                sc_debug(card->ctx, "called, cached path=%s\n", pbuf);
609        }
610
611        switch (path->type) {
612        case SC_PATH_TYPE_PATH:
613                if ((pathlen & 1) != 0) /* not divisible by 2 */
614                        return SC_ERROR_INVALID_ARGUMENTS;
615                magic_done = check_path(card, &pathptr, &pathlen, file_out != NULL);
616                if (pathlen == 0)
617                        return 0;
618                if (pathlen != 2 || memcmp(pathptr, "\x3F\x00", 2) != 0) {
619                        locked = 1;
620                        r = sc_lock(card);
621                        SC_TEST_RET(card->ctx, r, "sc_lock() failed");
622                        if (!magic_done && memcmp(pathptr, "\x3F\x00", 2) != 0) {
623                                r = select_file_id(card, (const u8 *) "\x3F\x00", 2, 0, NULL);
624                                if (r)
625                                        sc_unlock(card);
626                                SC_TEST_RET(card->ctx, r, "Unable to select Master File (MF)");
627                        }
628                        while (pathlen > 2) {
629                                r = select_file_id(card, pathptr, 2, 0, NULL);
630                                if (r)
631                                        sc_unlock(card);
632                                SC_TEST_RET(card->ctx, r, "Unable to select DF");
633                                pathptr += 2;
634                                pathlen -= 2;
635                        }
636                }
637                break;
638        case SC_PATH_TYPE_DF_NAME:
639                p1 = 0x04;
640                break;
641        case SC_PATH_TYPE_FILE_ID:
642                if (pathlen != 2)
643                        return SC_ERROR_INVALID_ARGUMENTS;
644                break;
645        }
646        r = select_file_id(card, pathptr, pathlen, p1, file_out);
647        if (locked)
648                sc_unlock(card);
649        cache_path(card, path, r);
650        SC_FUNC_RETURN(card->ctx, 2, r);
651}
652
653static int cryptoflex_list_files(sc_card_t *card, u8 *buf, size_t buflen)
654{
655        sc_apdu_t apdu;
656        u8 rbuf[4];
657        int r;
658        size_t count = 0;
659       
660        sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA8, 0, 0);
661        apdu.cla = 0xF0;
662        apdu.le = 4;
663        apdu.resplen = 4;
664        apdu.resp = rbuf;
665        while (buflen > 2) {
666                r = sc_transmit_apdu(card, &apdu);
667                if (r)
668                        return r;
669                if (apdu.sw1 == 0x6A && apdu.sw2 == 0x82)
670                        break;
671                r = sc_check_sw(card, apdu.sw1, apdu.sw2);
672                if (r)
673                        return r;
674                if (apdu.resplen != 4) {
675                        sc_error(card->ctx, "expected 4 bytes, got %d.\n", apdu.resplen);
676                        return SC_ERROR_UNKNOWN_DATA_RECEIVED;
677                }
678                memcpy(buf, rbuf + 2, 2);
679                buf += 2;
680                count += 2;
681                buflen -= 2;
682        }
683        return count;
684}
685
686/*
687 * The Cyberflex LIST FILES command is slightly different...
688 */
689static int cyberflex_list_files(sc_card_t *card, u8 *buf, size_t buflen)
690{
691        sc_apdu_t apdu;
692        u8 rbuf[6];
693        int r;
694        size_t count = 0, p2 = 0;
695       
696        while (buflen > 2) {
697                sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA8, 0, ++p2);
698                apdu.le = 6;
699                apdu.resplen = 6;
700                apdu.resp = rbuf;
701                r = sc_transmit_apdu(card, &apdu);
702                if (r)
703                        return r;
704                if (apdu.sw1 == 0x6A && apdu.sw2 == 0x83)
705                        break;
706                r = sc_check_sw(card, apdu.sw1, apdu.sw2);
707                if (r)
708                        return r;
709                if (apdu.resplen != 6) {
710                        sc_error(card->ctx, "expected 6 bytes, got %d.\n", apdu.resplen);
711                        return SC_ERROR_UNKNOWN_DATA_RECEIVED;
712                }
713                memcpy(buf, rbuf + 4, 2);
714                buf += 2;
715                count += 2;
716                buflen -= 2;
717        }
718        return count;
719}
720
721static int flex_delete_file(sc_card_t *card, const sc_path_t *path)
722{
723        sc_apdu_t apdu;
724        int r;
725
726        SC_FUNC_CALLED(card->ctx, 1);
727        if (path->type != SC_PATH_TYPE_FILE_ID && path->len != 2) {
728                sc_error(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID\n");
729                SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
730        }
731        sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00);
732        if (!IS_CYBERFLEX(card))
733                apdu.cla = 0xF0;        /* Override CLA byte */
734        apdu.data = path->value;
735        apdu.lc = 2;
736        apdu.datalen = 2;
737       
738        r = sc_transmit_apdu(card, &apdu);
739        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
740        return sc_check_sw(card, apdu.sw1, apdu.sw2);
741}
742
743static int acl_to_ac_nibble(const sc_acl_entry_t *e)
744{
745        if (e == NULL)
746                return -1;
747        if (e->next != NULL)    /* FIXME */
748                return -1;
749        switch (e->method) {
750        case SC_AC_NONE:
751                return 0x00;
752        case SC_AC_CHV:
753                switch (e->key_ref) {
754                case 1:
755                        return 0x01;
756                        break;
757                case 2:
758                        return 0x02;
759                        break;
760                }
761                return -1;
762        case SC_AC_PRO:
763                return 0x03;
764        case SC_AC_AUT:
765                return 0x04;
766        case SC_AC_NEVER:
767                return 0x0f;
768        }
769        return -1;
770}
771
772static int acl_to_keynum_nibble(const sc_acl_entry_t *e)
773{
774        while (e != NULL && e->method != SC_AC_AUT)
775                e = e->next;
776        if (e == NULL || e->key_ref == SC_AC_KEY_REF_NONE)
777                return 0;
778
779        return e->key_ref & 0x0F;
780}
781
782static int
783cryptoflex_construct_file_attrs(sc_card_t *card, const sc_file_t *file,
784                                 u8 *buf, size_t *buflen)
785{
786        u8 *p = buf;
787        int r, i;
788        int ops[6];
789
790        p[0] = 0xFF;
791        p[1] = 0xFF;
792        p[2] = file->size >> 8;
793        p[3] = file->size & 0xFF;
794        p[4] = file->id >> 8;
795        p[5] = file->id & 0xFF;
796        if (file->type == SC_FILE_TYPE_DF)
797                p[6] = 0x38;
798        else
799                switch (file->ef_structure) {
800                case SC_FILE_EF_TRANSPARENT:
801                        p[6] = 0x01;
802                        break;
803                case SC_FILE_EF_LINEAR_FIXED:
804                        p[6] = 0x02;
805                        break;
806                case SC_FILE_EF_LINEAR_VARIABLE:
807                        p[6] = 0x04;
808                        break;
809                case SC_FILE_EF_CYCLIC:
810                        p[6] = 0x06;
811                        break;
812                default:
813                        sc_error(card->ctx, "Invalid EF structure\n");
814                        return -1;
815                }
816        p[7] = 0xFF;    /* allow Decrease and Increase */
817        for (i = 0; i < 6; i++)
818                ops[i] = -1;
819        if (file->type == SC_FILE_TYPE_DF) {
820                ops[0] = SC_AC_OP_LIST_FILES;
821                ops[2] = SC_AC_OP_DELETE;
822                ops[3] = SC_AC_OP_CREATE;
823        } else {
824                ops[0] = SC_AC_OP_READ;
825                ops[1] = SC_AC_OP_UPDATE;
826                ops[2] = SC_AC_OP_READ;
827                ops[3] = SC_AC_OP_UPDATE;
828                ops[4] = SC_AC_OP_REHABILITATE;
829                ops[5] = SC_AC_OP_INVALIDATE;
830        }
831        p[8] = p[9] = p[10] = 0;
832        p[13] = p[14] = p[15] = 0; /* Key numbers */
833        for (i = 0; i < 6; i++) {
834                const sc_acl_entry_t *entry;
835                if (ops[i] == -1)
836                        continue;
837                entry = sc_file_get_acl_entry(file, ops[i]);
838                r = acl_to_ac_nibble(entry);
839                SC_TEST_RET(card->ctx, r, "Invalid ACL value");
840                /* Do some magic to get the nibbles right */
841                p[8 + i/2] |= (r & 0x0F) << (((i+1) % 2) * 4);
842                r = acl_to_keynum_nibble(entry);
843                p[13 + i/2] |= (r & 0x0F) << (((i+1) % 2) * 4);
844        }
845        p[11] = (file->status & SC_FILE_STATUS_INVALIDATED) ? 0x00 : 0x01;
846        if (file->type != SC_FILE_TYPE_DF &&
847            (file->ef_structure == SC_FILE_EF_LINEAR_FIXED ||
848            file->ef_structure == SC_FILE_EF_CYCLIC))
849                p[12] = 0x04;
850        else
851                p[12] = 0x03;
852        if (p[12] == 0x04) {
853                p[16] = file->record_length;
854                *buflen = 17;
855        } else
856                *buflen = 16;
857
858        return 0;
859}
860
861static int
862cyberflex_construct_file_attrs(sc_card_t *card, const sc_file_t *file,
863                                 u8 *buf, size_t *buflen)
864{
865        u8 *p = buf;
866        int i;
867        size_t size = file->size;
868        int ops[6];
869
870        /* cyberflex wants input parameters length added */
871        switch (file->type) {
872        case SC_FILE_TYPE_DF:
873                size += 24;
874                break;
875        case SC_FILE_TYPE_WORKING_EF:
876        default:
877                size += 16;
878                break;
879        }
880
881        sc_debug(card->ctx, "Creating %02x:%02x, size %d %02x:%02x\n",
882                 file->id >> 8,
883                 file->id & 0xFF,
884                 size,
885                 size >> 8,
886                 size & 0xFF);
887
888        p[0] = size >> 8;
889        p[1] = size & 0xFF;
890        p[2] = file->id >> 8;
891        p[3] = file->id & 0xFF;
892        if (file->type == SC_FILE_TYPE_DF)
893                p[4] = 0x20;
894        else
895                switch (file->ef_structure) {
896                case SC_FILE_EF_TRANSPARENT:
897                        p[4] = 0x02;
898                        break;
899                case SC_FILE_EF_LINEAR_FIXED:
900                        p[4] = 0x0C;
901                        break;
902                case SC_FILE_EF_LINEAR_VARIABLE:
903                        p[4] = 0x19;
904                        break;
905                case SC_FILE_EF_CYCLIC:
906                        p[4] = 0x1D;
907                        break;
908                default:
909                        sc_error(card->ctx, "Invalid EF structure\n");
910                        return -1;
911                }
912        p[5] = 0x01;    /* status?? */
913        for (i = 0; i < 6; i++)
914                ops[i] = -1;
915        if (file->type == SC_FILE_TYPE_DF) {
916                ops[0] = SC_AC_OP_LIST_FILES;
917                ops[2] = SC_AC_OP_DELETE;
918                ops[3] = SC_AC_OP_CREATE;
919        } else {
920                ops[0] = SC_AC_OP_READ;
921                ops[1] = SC_AC_OP_UPDATE;
922                ops[2] = SC_AC_OP_READ;
923                ops[3] = SC_AC_OP_UPDATE;
924                ops[4] = SC_AC_OP_REHABILITATE;
925                ops[5] = SC_AC_OP_INVALIDATE;
926        }
927        p[6] = p[7] = 0;
928       
929        *buflen = 16;
930
931        p[8] = p[9] = p[11] = 0xFF;
932        p[10] = p[12] = p[13] = p[14] = p[15] = 0x00;
933        return 0;
934}
935
936static int flex_create_file(sc_card_t *card, sc_file_t *file)
937{
938        u8 sbuf[18];
939        size_t sendlen;
940        int r, rec_nr;
941        sc_apdu_t apdu;
942       
943        /* Build the file attrs. These are not the real FCI bytes
944         * in the standard sense, but its a convenient way of
945         * abstracting the Cryptoflex/Cyberflex differences */
946        r = card->ops->construct_fci(card, file, sbuf, &sendlen);
947        if (r) {
948                sc_error(card->ctx, "File structure encoding failed.\n");
949                return SC_ERROR_INVALID_ARGUMENTS;
950        }
951        if (file->type != SC_FILE_TYPE_DF && file->ef_structure != SC_FILE_EF_TRANSPARENT)
952                rec_nr = file->record_count;
953        else
954                rec_nr = 0;
955        sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, rec_nr);
956        if (!IS_CYBERFLEX(card))
957                apdu.cla = 0xF0;
958        apdu.data = sbuf;
959        apdu.datalen = sendlen;
960        apdu.lc = sendlen;
961
962        r = sc_transmit_apdu(card, &apdu);
963        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
964        r = sc_check_sw(card, apdu.sw1, apdu.sw2);
965        SC_TEST_RET(card->ctx, r, "Card returned error");
966        if (card->cache_valid) {
967                u8 file_id[2];