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

Revision 3502, 32.1 kB (checked in by ludovic.rousseau, 7 months ago)

convert C++ in C comment

Line 
1/*
2 * Copyright (c) 2007  Athena Smartcard Solutions Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18
19#include "internal.h"
20#include <ctype.h>
21#include <string.h>
22
23#include "asn1.h"
24#include "cardctl.h"
25
26static const struct sc_card_operations *iso_ops = NULL;
27
28struct sc_card_operations asepcos_ops;
29static struct sc_card_driver asepcos_drv = {
30        "Athena ASEPCOS",
31        "asepcos",
32        &asepcos_ops,
33        NULL, 0, NULL
34};
35
36static struct sc_atr_table asepcos_atrs[] = {
37        { "3b:d6:18:00:81:b1:80:7d:1f:03:80:51:00:61:10:30:8f", NULL, NULL, SC_CARD_TYPE_ASEPCOS_GENERIC, 0, NULL},
38        { "3b:d6:18:00:81:b1:fe:7d:1f:03:41:53:45:37:35:35:01", NULL, NULL, SC_CARD_TYPE_ASEPCOS_JAVA, 0, NULL},
39        { NULL, NULL, NULL, 0, 0, NULL }
40};
41
42static int asepcos_finish(sc_card_t *card)
43{
44        return SC_SUCCESS;
45}
46
47static int asepcos_match_card(sc_card_t *card)
48{
49        int i = _sc_match_atr(card, asepcos_atrs, &card->type);
50        if (i < 0)
51                return 0;
52        return 1;
53}
54
55static int asepcos_select_asepcos_applet(sc_card_t *card)
56{
57        static const u8 asepcos_aid[] = {0xA0,0x00,0x00,0x01,0x64,0x41,0x53,0x45,0x50,0x43,0x4F,0x53,0x00};
58        sc_path_t tpath;
59        int       r;
60
61        tpath.type = SC_PATH_TYPE_DF_NAME;
62        tpath.len  = sizeof(asepcos_aid);
63        memcpy(tpath.value, asepcos_aid, sizeof(asepcos_aid));
64
65        r = sc_select_file(card, &tpath, NULL);
66        if (r != SC_SUCCESS) {
67                sc_error(card->ctx, "unable to select ASEPCOS applet");
68                return r;
69        }
70
71        return SC_SUCCESS;
72}
73
74static int asepcos_init(sc_card_t *card)
75{
76        unsigned long   flags;
77
78        card->name = "Athena ASEPCOS";
79        card->cla  = 0x00;
80
81        /* in case of a Java card try to select the ASEPCOS applet */
82        if (card->type == SC_CARD_TYPE_ASEPCOS_JAVA) {
83                int r = asepcos_select_asepcos_applet(card);
84                if (r != SC_SUCCESS)
85                        return r;
86        }
87
88        /* Set up algorithm info. */
89        flags = SC_ALGORITHM_RSA_RAW
90                | SC_ALGORITHM_RSA_HASH_NONE
91                | SC_ALGORITHM_ONBOARD_KEY_GEN
92                ;
93        _sc_card_add_rsa_alg(card,  512, flags, 0);
94        _sc_card_add_rsa_alg(card,  768, flags, 0);
95        _sc_card_add_rsa_alg(card, 1024, flags, 0);
96        _sc_card_add_rsa_alg(card, 1536, flags, 0);
97        _sc_card_add_rsa_alg(card, 1792, flags, 0);
98        _sc_card_add_rsa_alg(card, 2048, flags, 0);
99
100        card->caps |= SC_CARD_CAP_RSA_2048
101                      | SC_CARD_CAP_APDU_EXT
102                      | SC_CARD_CAP_USE_FCI_AC;
103
104        return SC_SUCCESS;
105}
106
107/* tables to map the asepcos access mode bytes to the OpenSC
108 * access mode flags */
109
110typedef struct {
111        unsigned int am;
112        unsigned int sc;
113} amode_entry_t;
114
115static const amode_entry_t df_amode_table[] = {
116        { 0x40, SC_AC_OP_DELETE_SELF }, /* DELETE self  */
117        { 0x01, SC_AC_OP_DELETE },      /* DELETE child */
118        { 0x10, SC_AC_OP_INVALIDATE },  /* DEACTIVATE FILE */
119        { 0x08, SC_AC_OP_REHABILITATE },/* ACTIVATE FILE   */
120        { 0x04, SC_AC_OP_CREATE },      /* CREATE DF    */
121        { 0x02, SC_AC_OP_CREATE },      /* CREATE EF    */
122        { 0, 0 }
123};
124
125static const amode_entry_t wef_amode_table[] = {
126        { 0x04, SC_AC_OP_WRITE },
127        { 0x02, SC_AC_OP_UPDATE },
128        { 0x01, SC_AC_OP_READ },
129        { 0, 0 },
130};
131
132static const amode_entry_t ief_amode_table[] = {
133        { 0x90, SC_AC_OP_REHABILITATE },
134        /* UPDATE is also used when a new key is generated */
135        { 0x82, SC_AC_OP_UPDATE },
136        { 0, 0 },
137};
138
139static int set_sec_attr(sc_file_t *file, unsigned int am, unsigned int ac,
140        unsigned int meth)
141{
142        const amode_entry_t *table;
143        if (file->type == SC_FILE_TYPE_DF)
144                table = df_amode_table;
145        else if (file->type == SC_FILE_TYPE_WORKING_EF)
146                table = wef_amode_table;
147        else if (file->type == SC_FILE_TYPE_INTERNAL_EF)
148                table = ief_amode_table;
149        else
150                return SC_ERROR_INVALID_ARGUMENTS;
151        for (; table->am != 0; table++) {
152                if (table->am & am)
153                        sc_file_add_acl_entry(file, table->sc, meth, ac);
154        }
155        return SC_SUCCESS;
156}
157
158/* Convert asepcos security attributes to opensc access conditions.
159 */
160static int asepcos_parse_sec_attr(sc_card_t *card, sc_file_t *file, const u8 *buf,
161        size_t len)
162{
163        const u8 *p = buf;
164
165        while (len != 0) {
166                unsigned int amode, tlen = 3;
167                if (len < 5 && p[0] != 0x80 && p[1] != 0x01) {
168                        sc_error(card->ctx, "invalid access mode encoding");
169                        return SC_ERROR_INTERNAL;
170                }
171                amode = p[2];
172                if (p[3] == 0x90 && p[4] == 0x00) {
173                        int r = set_sec_attr(file, amode, 0, SC_AC_NONE);
174                        if (r != SC_SUCCESS)
175                                return r;
176                        tlen += 2;
177                } else if (p[3] == 0x97 && p[4] == 0x00) {
178                        int r = set_sec_attr(file, amode, 0, SC_AC_NEVER);
179                        if (r != SC_SUCCESS)
180                                return r;
181                        tlen += 2;
182                } else if (p[3] == 0xA0 && len >= 4U + p[4]) {
183                        /* TODO: support OR expressions */
184                        int r = set_sec_attr(file, amode, p[5], SC_AC_CHV);
185                        if (r != SC_SUCCESS)
186                                return r;
187                        tlen += 2 + p[4]; /* FIXME */
188                } else if (p[3] == 0xAF && len >= 4U + p[4]) {
189                        /* TODO: support AND expressions */
190                        int r = set_sec_attr(file, amode, p[5], SC_AC_CHV);
191                        if (r != SC_SUCCESS)
192                                return r;
193                        tlen += 2 + p[4];       /* FIXME */
194                } else {
195                        sc_error(card->ctx, "invalid security condition");
196                        return SC_ERROR_INTERNAL;
197                }
198                p   += tlen;
199                len -= tlen;
200        }
201
202        return SC_SUCCESS;
203}
204
205/* sets a TLV encoded path as returned from GET DATA in a sc_path_t object
206 */
207static int asepcos_tlvpath_to_scpath(sc_path_t *out, const u8 *in, size_t in_len)
208{
209        int    r;
210        size_t len = in_len;
211
212        memset(out, 0, sizeof(sc_path_t));
213
214        while (len != 0) {
215                if (len < 4)
216                        return SC_ERROR_INTERNAL;
217                if (in[0] != 0x8b || in[1] != 0x02)
218                        return SC_ERROR_INVALID_ASN1_OBJECT;
219                /* append file id to the path */
220                r = sc_append_path_id(out, &in[2], 2);
221                if (r != SC_SUCCESS)
222                        return r;
223                len -= 4;
224                in  += 4;
225        }
226        out->type = SC_PATH_TYPE_PATH;
227
228        return SC_SUCCESS;
229}
230
231/* returns the currently selected DF (if a EF is currently selected
232 * it returns the path from the MF to the DF in which the EF is
233 * located.
234 * @param  card  sc_card_t object to use
235 * @param  path  OUT path from the MF to the current DF
236 * @return SC_SUCCESS on success and an error value otherwise
237 */
238static int asepcos_get_current_df_path(sc_card_t *card, sc_path_t *path)
239{
240        int r;
241        sc_apdu_t apdu;
242        u8        rbuf[SC_MAX_APDU_BUFFER_SIZE];
243
244        sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x83);
245        apdu.resp    = rbuf;
246        apdu.resplen = sizeof(rbuf);
247        apdu.le      = 256;
248
249        r = sc_transmit_apdu(card, &apdu);
250        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
251        if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
252                return sc_check_sw(card, apdu.sw1, apdu.sw2);
253        return asepcos_tlvpath_to_scpath(path, apdu.resp, apdu.resplen);
254}
255
256/* SELECT FILE: call the ISO SELECT FILE implementation and parse
257 * asepcos specific security attributes.
258 */
259static int asepcos_select_file(sc_card_t *card, const sc_path_t *in_path,
260        sc_file_t **file)
261{
262        int       r;
263        sc_path_t npath = *in_path;
264
265        SC_FUNC_CALLED(card->ctx, 2);
266
267        if (in_path->type == SC_PATH_TYPE_PATH) {
268                /* check the current DF to avoid unnecessary re-selection of
269                 * the MF (as this might invalidate a security status) */
270                sc_path_t tpath;
271
272                r = asepcos_get_current_df_path(card, &tpath);
273                /* workaround: as opensc can't handle paths with file id
274                 * and application names in it let's ignore the current
275                 * DF if the returned path contains a unsupported tag.
276                 */
277                if (r != SC_ERROR_INVALID_ASN1_OBJECT && r != SC_SUCCESS)
278                        return r;
279                if (r == SC_SUCCESS && sc_compare_path_prefix(&tpath, &npath) != 0) {
280                        /* remove the currently selected DF from the path */
281                        if (tpath.len == npath.len) {
282                                /* we are already in the requested DF */
283                                if (file == NULL)
284                                        /* no file information requested =>
285                                         * nothing to do */
286                                        return SC_SUCCESS;
287                        } else {
288                                /* shorten path */
289                                r = sc_path_set(&npath, 0, &in_path->value[tpath.len],
290                                                npath.len - tpath.len, 0, 0);
291                                if (r != SC_SUCCESS)
292                                        return r;
293                                if (npath.len == 2)
294                                        npath.type = SC_PATH_TYPE_FILE_ID;
295                                else
296                                        npath.type = SC_PATH_TYPE_PATH;
297                        }
298                }
299        }
300
301        r = iso_ops->select_file(card, &npath, file);
302        /* XXX: this doesn't look right */
303        if (file != NULL && *file != NULL)
304                if ((*file)->ef_structure == SC_FILE_EF_UNKNOWN)
305                        (*file)->ef_structure = SC_FILE_EF_TRANSPARENT;
306        if (r == SC_SUCCESS && file != NULL) {
307                r = asepcos_parse_sec_attr(card, *file, (*file)->sec_attr, (*file)->sec_attr_len);
308                if (r != SC_SUCCESS)
309                        sc_error(card->ctx, "error parsing security attributes");
310        }
311        SC_FUNC_RETURN(card->ctx, 1, r);
312}
313
314static int asepcos_set_security_env(sc_card_t *card,
315        const sc_security_env_t *env, int se_num)
316{
317#if 0
318        /* this function doesn't seem to be necessary if RSA ENCRYPT DECRYPT
319         * is used.  */
320
321        sc_apdu_t apdu;
322        u8 sbuf[SC_MAX_APDU_BUFFER_SIZE], *p = sbuf;
323        int r, locked = 0;
324
325        SC_FUNC_CALLED(card->ctx, 1);
326        sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0);
327        switch (env->operation) {
328        case SC_SEC_OPERATION_DECIPHER:
329                apdu.p1 = 0x41;
330                apdu.p2 = 0xB8;
331                break;
332        case SC_SEC_OPERATION_SIGN:
333                apdu.p1 = 0x41;
334                apdu.p2 = 0xB6;
335                break;
336        default:
337                return SC_ERROR_INVALID_ARGUMENTS;
338        }
339        if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) {
340                *p++ = 0x80;    /* algorithm reference */
341                *p++ = 0x01;
342                *p++ = env->algorithm_ref & 0xFF;
343        }
344        if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) {
345                *p++ = 0x84;
346                *p++ = env->file_ref.len;
347                memcpy(p, env->file_ref.value, env->file_ref.len);
348                p += env->file_ref.len;
349        }
350
351        apdu.lc      = p - sbuf;
352        apdu.datalen = p - sbuf;
353        apdu.data    = sbuf;
354        if (se_num > 0) {
355                r = sc_lock(card);
356                SC_TEST_RET(card->ctx, r, "sc_lock() failed");
357                locked = 1;
358        }
359        if (apdu.datalen != 0) {
360                r = sc_transmit_apdu(card, &apdu);
361                if (r) {
362                        sc_perror(card->ctx, r, "APDU transmit failed");
363                        goto err;
364                }
365                r = sc_check_sw(card, apdu.sw1, apdu.sw2);
366                if (r) {
367                        sc_perror(card->ctx, r, "Card returned error");
368                        goto err;
369                }
370        }
371        if (se_num <= 0)
372                return 0;
373        sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xF2, se_num);
374        r = sc_transmit_apdu(card, &apdu);
375        sc_unlock(card);
376        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
377        return sc_check_sw(card, apdu.sw1, apdu.sw2);
378err:
379        if (locked)
380                sc_unlock(card);
381        return r;
382#else
383        return SC_SUCCESS;
384#endif
385}
386
387
388static int asepcos_akn_to_fileid(sc_card_t *card, sc_cardctl_asepcos_akn2fileid_t *p)
389{
390        int r;
391        u8  sbuf[32], rbuf[SC_MAX_APDU_BUFFER_SIZE];
392        sc_apdu_t apdu;
393
394        sbuf[0] = p->akn & 0xff;
395        sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x28, 0x02, 0x01);
396        apdu.cla    |= 0x80;
397        apdu.resp    = rbuf;
398        apdu.resplen = sizeof(rbuf);
399        apdu.le      = 256;
400        apdu.lc      = 1;
401        apdu.datalen = 1;
402        apdu.data    = sbuf;
403
404        r = sc_transmit_apdu(card, &apdu);
405        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
406
407        if (apdu.resplen != 4)
408                return SC_ERROR_INTERNAL;
409
410        p->fileid = (apdu.resp[1] << 16) | (apdu.resp[2] << 8) | apdu.resp[3];
411
412        return SC_SUCCESS;
413}
414
415/* sets the security attribute of a EF/DF
416 */
417static int asepcos_set_sec_attributes(sc_card_t *card, const u8 *data, size_t len,
418        int is_ef)
419{
420        int r, type = is_ef != 0 ? 0x02 : 0x04;
421        sc_apdu_t apdu;
422
423        sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x8a, type, 0xab);
424        apdu.cla    |= 0x80;
425        apdu.lc      = len;
426        apdu.datalen = len;
427        apdu.data    = data;
428        r = sc_transmit_apdu(card, &apdu);
429        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
430        return sc_check_sw(card, apdu.sw1, apdu.sw2);
431}
432
433/* encodes the opensc file attributes into the card specific format
434 */
435static int asepcos_set_security_attributes(sc_card_t *card, sc_file_t *file)
436{
437        size_t i;
438        const amode_entry_t *table;
439        u8     buf[64], *p = buf;
440        int    r = SC_SUCCESS;
441
442        /* first check wether the security attributes in encoded form
443         * are already set. If present use these */
444        if (file->sec_attr != NULL && file->sec_attr_len != 0)
445                return asepcos_set_sec_attributes(card, file->sec_attr,
446                                file->sec_attr_len, file->type == SC_FILE_TYPE_DF ? 0:1);
447        /* otherwise construct the ACL from the opensc ACLs */
448        if (file->type == SC_FILE_TYPE_DF)
449                table = df_amode_table;
450        else if (file->type == SC_FILE_TYPE_WORKING_EF)
451                table = wef_amode_table;
452        else if (file->type == SC_FILE_TYPE_INTERNAL_EF)
453                table = ief_amode_table;
454        else
455                return SC_ERROR_INVALID_ARGUMENTS;
456
457        p = buf;
458        for (i = 0; table[i].am != 0; i++) {
459                const struct sc_acl_entry *ent = sc_file_get_acl_entry(file, table[i].sc);
460                if (ent == NULL)
461                        continue;
462                *p++ = 0x80;
463                *p++ = 0x01;
464                *p++ = table[i].am & 0xff;
465                if (ent->method == SC_AC_NONE) {
466                        *p++ = 0x90;
467                        *p++ = 0x00;
468                } else if (ent->method == SC_AC_NEVER) {
469                        *p++ = 0x97;
470                        *p++ = 0x00;
471                } else if (ent->method == SC_AC_CHV) {
472                        sc_cardctl_asepcos_akn2fileid_t st;
473                        st.akn = ent->key_ref;
474                        r = asepcos_akn_to_fileid(card, &st);
475                        if (r != SC_SUCCESS)
476                                return r;
477                        *p++ = 0xa0;
478                        *p++ = 0x05;
479                        *p++ = 0x89;
480                        *p++ = 0x03;
481                        *p++ = (st.fileid >> 16) & 0xff;
482                        *p++ = (st.fileid >> 8 ) & 0xff;
483                        *p++ = st.fileid & 0xff;
484                } else {
485                        sc_error(card->ctx, "unknow auth method: '%d'", ent->method);
486                        return SC_ERROR_INTERNAL;
487                }
488        }
489
490        if (p != buf)
491                r = asepcos_set_sec_attributes(card, buf, p-buf, file->type == SC_FILE_TYPE_DF ? 0:1);
492        return r;
493}
494
495static int asepcos_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_len,
496        u8 * out, size_t outlen)
497{
498        int       r;
499        sc_apdu_t apdu;
500
501        SC_FUNC_CALLED(card->ctx, 2);
502
503        /* call RSA ENCRYPT DECRYPT for the decipher operation */
504        sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x14, 0x01, 0x00);
505        apdu.cla    |= 0x80;
506        apdu.resp    = out;
507        apdu.resplen = outlen;
508        /* if less than 256 bytes are expected than set Le to 0x00
509         * to tell the card the we want everything available (note: we
510         * always have Le <= crgram_len) */
511        apdu.le      = (outlen >= 256 && crgram_len < 256) ? 256 : outlen;
512        apdu.sensitive = 1;
513       
514        apdu.data    = crgram;
515        apdu.lc      = crgram_len;
516        apdu.datalen = crgram_len;
517        r = sc_transmit_apdu(card, &apdu);
518        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
519        if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
520                SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2));
521        return apdu.resplen;
522}
523
524/* compute the signature. Currently the RSA ENCRYPT DECRYPT command
525 * is used here (TODO: use the key attributes to determine method
526 * to use for signature generation).
527 */
528static int asepcos_compute_signature(sc_card_t *card, const u8 *data, size_t datalen,
529                         u8 *out, size_t outlen)
530{
531        int r = SC_SUCCESS, atype;
532        u8  rbuf[SC_MAX_APDU_BUFFER_SIZE];
533        sc_apdu_t apdu;
534
535        SC_FUNC_CALLED(card->ctx, 2);
536
537        if (datalen >= 256)
538                atype = SC_APDU_CASE_4_EXT;
539        else
540                atype = SC_APDU_CASE_4_SHORT;
541        sc_format_apdu(card, &apdu, atype, 0x14, 0x01, 0x00);
542        apdu.cla    |= 0x80;
543        apdu.lc      = datalen;
544        apdu.datalen = datalen;
545        apdu.data    = data;
546        apdu.resp    = rbuf;
547        apdu.resplen = sizeof(rbuf);
548        apdu.le      = 256;
549
550        r = sc_transmit_apdu(card, &apdu);
551        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
552        if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) {
553                sc_error(card->ctx, "error creating signature");
554                return sc_check_sw(card, apdu.sw1, apdu.sw2);
555        }
556
557        if (apdu.resplen > outlen)
558                return SC_ERROR_BUFFER_TOO_SMALL;
559        memcpy(out, apdu.resp, apdu.resplen);
560
561        return apdu.resplen;
562}
563
564/* activates the EF/DF specified in the file id.
565 */
566static int asepcos_activate_file(sc_card_t *card, int fileid, int is_ef)
567{
568        int r, type = is_ef != 0 ? 2 : 1;
569        sc_apdu_t apdu;
570        u8 sbuf[2];
571
572        sbuf[0] = (fileid >> 8) & 0xff;
573        sbuf[1] = fileid & 0xff;
574        sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x44, type, 0x00);
575        apdu.lc      = 2;
576        apdu.datalen = 2;
577        apdu.data    = sbuf;
578        r = sc_transmit_apdu(card, &apdu);
579        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
580        return sc_check_sw(card, apdu.sw1, apdu.sw2);
581}
582
583/* CREATE FILE: creates wEF, iEF and DFs. Note: although the ISO
584 * command is used for wEF and iEF so format of the data send to
585 * the card is asepcos specific.
586 * @param  card  the sc_card_t object to use
587 * @param  file  sc_file_t object describing the file to create
588 * @return SC_SUCCESS on success and an error code otherwise.
589 */
590static int asepcos_create_file(sc_card_t *card, sc_file_t *file)
591{
592        if (file->type == SC_FILE_TYPE_DF) {
593                int r, type;
594                sc_apdu_t apdu;
595                u8  sbuf[SC_MAX_APDU_BUFFER_SIZE], *p = &sbuf[0];
596
597                *p++ = (file->id >> 8) & 0xff;
598                *p++ = file->id & 0xff;
599                if (file->size > 0xffff) {
600                        *p++ = (file->size >> 24) & 0xff;
601                        *p++ = (file->size >> 16) & 0xff;
602                        *p++ = (file->size >> 8 ) & 0xff;
603                        *p++ = file->size & 0xff;
604                        type = 1;
605                } else {
606                        *p++ = (file->size >> 8) & 0xff;
607                        *p++ = file->size & 0xff;
608                        type = 0;
609                }
610                if (file->namelen != 0 && file->namelen <= 16) {
611                        memcpy(p, file->name, file->namelen);
612                        p += file->namelen;
613                }
614                sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xe0, 0x38, type);
615                apdu.cla    |= 0x80;
616                apdu.lc      = p - sbuf;
617                apdu.datalen = p - sbuf;
618                apdu.data    = sbuf;
619
620                r = sc_transmit_apdu(card, &apdu);
621                SC_TEST_RET(card->ctx, r, "APDU transmit failed");
622                if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
623                        return sc_check_sw(card, apdu.sw1, apdu.sw2);
624
625                r = sc_select_file(card, &file->path, NULL);
626                if (r != SC_SUCCESS)
627                        return r;
628                /* set security attributes */
629                r = asepcos_set_security_attributes(card, file);
630                if (r != SC_SUCCESS) {
631                        sc_error(card->ctx, "unable to set security attributes");
632                        return r;
633                }
634                return SC_SUCCESS;
635        } else if (file->type == SC_FILE_TYPE_WORKING_EF) {
636                int r;
637                sc_apdu_t apdu;
638                u8  descr_byte = file->ef_structure & 7;
639                u8  sbuf[SC_MAX_APDU_BUFFER_SIZE], *p = &sbuf[0];
640
641                *p++ = 0x85;
642                p++;
643                /* file id  */
644                *p++ = (file->id >> 8) & 0xff;
645                *p++ = file->id & 0xff;
646                /* record size */
647                if (file->ef_structure == SC_FILE_EF_TRANSPARENT) {
648                        *p++ = 0x00;
649                        *p++ = 0x00;
650                } else {
651                        *p++ = (file->record_length >> 8) & 0xff;
652                        *p++ = file->record_length & 0xff;
653                }
654                /* number of records or file size */
655                if (file->ef_structure == SC_FILE_EF_TRANSPARENT) {
656                        *p++ = (file->size >> 8) & 0xff;
657                        *p++ = file->size & 0xff;
658                } else {
659                        *p++ = (file->record_count >> 8) & 0xff;
660                        *p++ = file->record_count & 0xff;
661                }
662                /* set the length of the inner TLV object */
663                sbuf[1] = p - sbuf - 2;         /* FIXME */
664
665                sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xe0, descr_byte, 0x00);
666                apdu.lc      = p - sbuf;
667                apdu.datalen = p - sbuf;
668                apdu.data    = sbuf;
669                r = sc_transmit_apdu(card, &apdu);
670                SC_TEST_RET(card->ctx, r, "APDU transmit failed");
671                if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
672                        return sc_check_sw(card, apdu.sw1, apdu.sw2);
673
674                /* set security attributes */
675                r = asepcos_set_security_attributes(card, file);
676                if (r != SC_SUCCESS) {
677                        sc_error(card->ctx, "unable to set security attributes");
678                        return r;
679                }
680                return asepcos_activate_file(card, file->id, 1);
681        } else if (file->type == SC_FILE_TYPE_INTERNAL_EF) {
682                /* for internal EF we 'misuse' the prop_attr field of the
683                 * sc_file_t object to store the data send to the card in
684                 * the CREATE EF call.
685                 */
686                int r, atype = SC_APDU_CASE_3_SHORT;
687                sc_apdu_t apdu;
688
689                if (file->prop_attr_len > 255)
690                        atype = SC_APDU_CASE_3_EXT;
691
692                sc_format_apdu(card, &apdu, atype, 0xe0, 0x08, 0x00);
693                apdu.lc      = file->prop_attr_len;
694                apdu.datalen = file->prop_attr_len;
695                apdu.data    = file->prop_attr;
696
697                r = sc_transmit_apdu(card, &apdu);
698                SC_TEST_RET(card->ctx, r, "APDU transmit failed");
699                if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
700                        return sc_check_sw(card, apdu.sw1, apdu.sw2);
701                /* set security attributes */
702                r = asepcos_set_security_attributes(card, file);
703                if (r != SC_SUCCESS) {
704                        sc_error(card->ctx, "unable to set security attributes");
705                        return r;
706                }
707                return asepcos_activate_file(card, file->id, 1);
708        } else
709                return SC_ERROR_INVALID_ARGUMENTS;
710}
711
712/* list files: the function first calls GET DATA to get the current
713 * working DF. It then re-selects the DF to get proprietary FCI which
714 * contain the FID of the first child DF EF.
715 * The FID of the other EFs/DFs within the selected DF are then
716 * obtained by selecting the know FIDs to get next child EF/DF.
717 * @param  card  the sc_card_t object to use
718 * @param  buff  the output buffer for the list of FIDs
719 * @param  blen  the length of the buffer
720 * @return the number of FIDs read on success and an error value otherwise.
721 */
722static int asepcos_list_files(sc_card_t *card, u8 *buf, size_t blen)
723{
724        int       r, rv = 0, dfFID, efFID;
725        sc_path_t bpath, tpath;
726        sc_file_t *tfile = NULL;
727
728        /* 1. get currently selected DF */
729        r = asepcos_get_current_df_path(card, &bpath);
730        if (rv != SC_SUCCESS)
731                return r;
732        /* 2. re-select DF to get the FID of the child EFs/DFs */
733        r = sc_select_file(card, &bpath, &tfile);
734        if (r != SC_SUCCESS)
735                return r;
736        if (tfile->prop_attr_len != 6 || tfile->prop_attr == NULL) {
737                sc_file_free(tfile);
738                sc_error(card->ctx, "unable to parse proprietary FCI attributes");
739                return SC_ERROR_INTERNAL;
740        }
741        dfFID = (tfile->prop_attr[2] << 8) | tfile->prop_attr[3];
742        efFID = (tfile->prop_attr[4] << 8) | tfile->prop_attr[5];
743        sc_file_free(tfile);
744        /* 3. select every child DF to get the FID of the next child DF */
745        while (dfFID != 0) {
746                /* put DF FID on the list */
747                if (blen < 2)
748                        return SC_ERROR_BUFFER_TOO_SMALL;
749                *buf++ = (dfFID >> 8) & 0xff;
750                *buf++ = dfFID & 0xff;
751                rv   += 2;
752                blen -= 2;
753                /* select DF to get next DF FID */
754                tpath = bpath;
755                r = sc_append_file_id(&tpath, dfFID);
756                if (r != SC_SUCCESS)
757                        return r;
758                r = sc_select_file(card, &tpath, &tfile);
759                if (r != SC_SUCCESS)
760                        return r;
761                if (tfile->prop_attr_len != 6 || tfile->prop_attr == NULL)
762                        return SC_ERROR_INTERNAL;
763                dfFID = (tfile->prop_attr[0] << 8) | tfile->prop_attr[1];
764                sc_file_free(tfile);
765        }
766        /* 4. select every child EF ... */
767        while (efFID != 0) {
768                /* put DF FID on the list */
769                if (blen < 2)
770                        return SC_ERROR_BUFFER_TOO_SMALL;
771                *buf++ = (efFID >> 8) & 0xff;
772                *buf++ = efFID & 0xff;
773                rv   += 2;
774                blen -= 2;
775                /* select EF to get next EF FID */
776                tpath = bpath;
777                r = sc_append_file_id(&tpath, efFID);
778                if (r != SC_SUCCESS)
779                        return r;
780                r = sc_select_file(card, &tpath, &tfile);
781                if (r != SC_SUCCESS)
782                        return r;
783                if (tfile->prop_attr_len < 2 || tfile->prop_attr == NULL)
784                        return SC_ERROR_INTERNAL;
785                efFID = (tfile->prop_attr[0] << 8) | tfile->prop_attr[1];
786                sc_file_free(tfile);
787        }
788
789        return rv;
790}
791
792static int asepcos_delete_file(sc_card_t *card, const sc_path_t *path)
793{
794        int       r, ftype, atype;
795        sc_apdu_t apdu;
796        u8        buf[SC_MAX_APDU_BUFFER_SIZE];
797
798#if 0
799        /* select the file (note: if the file is already selected we do
800         * not re-select it as we might otherwise lose the necessary
801         * credential */
802        r = sc_select_file(card, path, NULL);
803        if (r != SC_SUCCESS)
804                return r;
805#endif
806        /* use GET DATA to determine whether it is a DF or EF */
807        sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x84);
808        apdu.le      = 256;
809        apdu.resplen = sizeof(buf);
810        apdu.resp    = buf;
811        r = sc_transmit_apdu(card, &apdu);
812        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
813        if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
814                /* looks like a EF */
815                atype = SC_APDU_CASE_3_SHORT;
816                ftype = 0x02;
817                buf[0] = path->value[path->len-2];
818                buf[1] = path->value[path->len-1];
819        } else {
820                /* presumedly a DF */
821                atype = SC_APDU_CASE_1;
822                ftype = 0x00;
823        }
824       
825        sc_format_apdu(card, &apdu, atype, 0xe4, ftype, 0x00);
826        if (atype == SC_APDU_CASE_3_SHORT) {
827                apdu.lc      = 2;
828                apdu.datalen = 2;
829                apdu.data    = buf;
830        }
831       
832        r = sc_transmit_apdu(card, &apdu);
833        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
834        return sc_check_sw(card, apdu.sw1, apdu.sw2);
835}
836
837/* returns the default transport key (note: this should be put in the
838 * pkcs15 profile file).
839 */
840static int asepcos_get_default_key(sc_card_t *card,
841        struct sc_cardctl_default_key *data)
842{
843        static const u8 asepcos_def_key[] = {0x41,0x53,0x45,0x43,0x41,0x52,0x44,0x2b};
844        if (data->method != SC_AC_CHV && data->method != SC_AC_AUT)
845                return SC_ERROR_NO_DEFAULT_KEY;
846        if (data->key_data == NULL || data->len < sizeof(asepcos_def_key))
847                return SC_ERROR_BUFFER_TOO_SMALL;
848        memcpy(data->key_data, asepcos_def_key, sizeof(asepcos_def_key));
849        data->len = sizeof(asepcos_def_key);
850        return SC_SUCCESS;
851}
852
853static int asepcos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial)
854{
855        int r;
856        sc_apdu_t apdu;
857        u8  rbuf[SC_MAX_APDU_BUFFER_SIZE];
858
859        sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x14);
860        apdu.resp = rbuf;
861        apdu.resplen = sizeof(rbuf);
862        apdu.le   = 256;
863        r = sc_transmit_apdu(card, &apdu);
864        SC_TEST_RET(card->ctx, r,  "APDU transmit failed");
865        if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
866                return SC_ERROR_INTERNAL;
867        if (apdu.resplen != 8) {
868                sc_debug(card->ctx, "unexpected response to GET DATA serial number\n");
869                return SC_ERROR_INTERNAL;
870        }
871        /* cache serial number */
872        memcpy(card->serialnr.value, rbuf, 8);
873        card->serialnr.len = 8;
874        /* copy and return serial number */
875        memcpy(serial, &card->serialnr, sizeof(*serial));
876        return SC_SUCCESS;
877}
878
879static int asepcos_change_key(sc_card_t *card, sc_cardctl_asepcos_change_key_t *p)
880{
881        int       r, atype;
882        sc_apdu_t apdu;
883
884        if (p->datalen > 255)
885                atype = SC_APDU_CASE_3_EXT;
886        else
887                atype = SC_APDU_CASE_3_SHORT;
888
889        sc_format_apdu(card, &apdu, atype, 0x24, 0x01, 0x80);
890        apdu.lc      = p->datalen;
891        apdu.datalen = p->datalen;
892        apdu.data    = p->data;
893
894        r = sc_transmit_apdu(card, &apdu);
895        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
896        return sc_check_sw(card, apdu.sw1, apdu.sw2);
897}
898
899static int asepcos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
900{
901        switch (cmd) {
902        case SC_CARDCTL_GET_DEFAULT_KEY:
903                return asepcos_get_default_key(card, (struct sc_cardctl_default_key *) ptr);
904        case SC_CARDCTL_GET_SERIALNR:
905                return asepcos_get_serialnr(card, (sc_serial_number_t *)ptr);
906        case SC_CARDCTL_ASEPCOS_CHANGE_KEY:
907                return asepcos_change_key(card, (sc_cardctl_asepcos_change_key_t*)ptr);
908        case SC_CARDCTL_ASEPCOS_AKN2FILEID:
909                return asepcos_akn_to_fileid(card, (sc_cardctl_asepcos_akn2fileid_t*)ptr);
910        case SC_CARDCTL_ASEPCOS_SET_SATTR:
911                return asepcos_set_security_attributes(card, (sc_file_t*)ptr);
912        case SC_CARDCTL_ASEPCOS_ACTIVATE_FILE:
913                return asepcos_activate_file(card, ((sc_cardctl_asepcos_activate_file_t*)ptr)->fileid,
914                                           ((sc_cardctl_asepcos_activate_file_t *)ptr)->is_ef);
915        }
916        return SC_ERROR_NOT_SUPPORTED;
917}
918
919/* build the different APDUs for the PIN handling commands
920 */
921static int asepcos_build_pin_apdu(sc_card_t *card, sc_apdu_t *apdu,
922        struct sc_pin_cmd_data *data, u8 *buf, size_t buf_len,
923        unsigned int cmd, int is_puk)
924{
925        int r, fileid;
926        u8  *p = buf;
927        sc_cardctl_asepcos_akn2fileid_t st;
928
929        switch (cmd) {
930        case SC_PIN_CMD_VERIFY:
931                st.akn = data->pin_reference;
932                r = asepcos_akn_to_fileid(card, &st);
933                if (r != SC_SUCCESS)
934                        return r;
935                fileid = st.fileid;
936                /* the fileid of the puk is the fileid of the pin + 1 */
937                if (is_puk != 0)
938                        fileid++;
939                sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, 0x20, 0x02, 0x80);
940                *p++ = (fileid >> 24) & 0xff;
941                *p++ = (fileid >> 16) & 0xff;
942                *p++ = (fileid >> 8 ) & 0xff;
943                *p++ = fileid & 0xff;
944                if (is_puk == 0) {
945                        memcpy(p, data->pin1.data, data->pin1.len);
946                        p += data->pin1.len;
947                } else {
948                        memcpy(p, data->pin1.data, data->pin1.len);
949                        p += data->pin1.len;
950                }
951                apdu->lc       = p - buf;
952                apdu->datalen  = p - buf;
953                apdu->data     = buf;
954                break;
955        case SC_PIN_CMD_CHANGE:
956                /* build the CHANGE KEY apdu. Note: the PIN file is implicitly
957                 * selected by its SFID */
958                *p++ = 0x81;
959                *p++ = data->pin2.len & 0xff;
960                memcpy(p, data->pin2.data, data->pin2.len);
961                p   += data->pin2.len;
962                st.akn = data->pin_reference;
963                r = asepcos_akn_to_fileid(card, &st);
964                if (r != SC_SUCCESS)
965                        return r;
966                fileid = 0x80 | (st.fileid & 0x1f);
967                sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, fileid);
968                apdu->lc       = p - buf;
969                apdu->datalen  = p - buf;
970                apdu->data     = buf;
971