diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 5728df11..1dbc4f7a 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -29,7 +29,7 @@ libopensc_la_SOURCES = \ card-cardos.c card-tcos.c card-emv.c card-default.c \ card-mcrd.c card-starcos.c card-openpgp.c card-jcop.c \ card-oberthur.c card-belpic.c card-atrust-acos.c \ - card-incrypto34.c card-piv.c card-muscle.c \ + card-incrypto34.c card-piv.c card-muscle.c card-acos5.c \ \ pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \ pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafe.c \ diff --git a/src/libopensc/card-acos5.c b/src/libopensc/card-acos5.c new file mode 100644 index 00000000..a2558d9b --- /dev/null +++ b/src/libopensc/card-acos5.c @@ -0,0 +1,239 @@ +/* + * card-acos5.c: Support for ACS ACOS5 cards. + * + * Copyright (C) 2007 Ian A. Young + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "internal.h" +#include "cardctl.h" + +static struct sc_atr_table acos5_atrs[] = { + {"3b:be:18:00:00:41:05:10:00:00:00:00:00:00:00:00:00:90:00", NULL, NULL, + SC_CARD_TYPE_TEST_BASE, 0, NULL}, + {NULL, NULL, NULL, 0, 0, NULL} +}; + +static struct sc_card_operations *iso_ops; +static struct sc_card_operations acos5_ops; +static struct sc_card_driver acos5_drv = { + "ACS ACOS5 card", + "acos5", + &acos5_ops, + NULL, 0, NULL +}; + +static int acos5_match_card(sc_card_t * card) +{ + int i; + + i = _sc_match_atr(card, acos5_atrs, &card->type); + if (i < 0) + return 0; + return 1; +} + +static int acos5_init(sc_card_t * card) +{ + card->max_recv_size = 128; + card->max_send_size = 128; + return SC_SUCCESS; +} + +static int acos5_finish(sc_card_t * card) +{ + return SC_SUCCESS; +} + +static int acos5_select_file_by_path(sc_card_t * card, + const sc_path_t * in_path, + sc_file_t ** file_out) +{ + int in_len = in_path->len; + const u8 *in_pos = in_path->value; + + sc_path_t path; + path.len = 2; /* one component at a time */ + path.type = SC_PATH_TYPE_FILE_ID; + + /* + * Check parameters. + */ + if (in_len % 2 != 0) + return SC_ERROR_INVALID_ARGUMENTS; + + /* + * File ID by file ID... + */ + while (in_len) { + int result; + memcpy(path.value, in_pos, 2); + result = iso_ops->select_file(card, &path, file_out); + if (result != SC_SUCCESS) + return result; + in_len -= 2; + in_pos += 2; + } + return SC_SUCCESS; +} + +static int acos5_select_file(sc_card_t * card, + const sc_path_t * in_path, sc_file_t ** file_out) +{ + switch (in_path->type) { + + case SC_PATH_TYPE_PATH: + return acos5_select_file_by_path(card, in_path, file_out); + + default: + return iso_ops->select_file(card, in_path, file_out); + } +} + +static int acos5_get_serialnr(sc_card_t * card, sc_serial_number_t * serial) +{ + int r; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + sc_apdu_t apdu; + + /* + * Check arguments. + */ + if (!serial) + return SC_ERROR_INVALID_ARGUMENTS; + + /* + * Return a cached serial number, if we have one. + */ + if (card->serialnr.len) { + memcpy(serial, &card->serialnr, sizeof(*serial)); + return SC_SUCCESS; + } + + /* + * Fetch serial number using GET CARD INFO. + */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x14, 0, 0); + apdu.cla |= 0x80; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 6; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) + return SC_ERROR_INTERNAL; + + /* + * Cache serial number. + */ + memcpy(card->serialnr.value, apdu.resp, apdu.resplen); + card->serialnr.len = apdu.resplen; + + /* + * Copy and return serial number. + */ + memcpy(serial, &card->serialnr, sizeof(*serial)); + return SC_SUCCESS; +} + +static int acos5_card_ctl(sc_card_t * card, unsigned long cmd, void *ptr) +{ + switch (cmd) { + + case SC_CARDCTL_GET_SERIALNR: + return acos5_get_serialnr(card, (sc_serial_number_t *) ptr); + + default: + return SC_ERROR_NOT_SUPPORTED; + } +} + +static int acos5_list_files(sc_card_t * card, u8 * buf, size_t buflen) +{ + sc_apdu_t apdu; + int r; + size_t count; + u8 *bufp = buf; /* pointer into buf */ + int fno = 0; /* current file index */ + + /* + * Check parameters. + */ + if (!buf || (buflen & 1)) + return SC_ERROR_INVALID_ARGUMENTS; + + /* + * Use CARD GET INFO to fetch the number of files under the + * curently selected DF. + */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x14, 0x01, 0x00); + apdu.cla |= 0x80; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (apdu.sw1 != 0x90) + return SC_ERROR_INTERNAL; + count = apdu.sw2; + + while (count--) { + u8 info[8]; + + /* + * Truncate the scan if no more room left in output buffer. + */ + if (buflen == 0) + break; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x14, 0x02, + fno++); + apdu.cla |= 0x80; + apdu.resp = info; + apdu.resplen = sizeof(info); + apdu.le = sizeof(info); + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) + return SC_ERROR_INTERNAL; + + *bufp++ = info[2]; + *bufp++ = info[3]; + buflen -= 2; + } + + return (bufp - buf); +} + +static struct sc_card_driver *sc_get_driver(void) +{ + struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); + + iso_ops = iso_drv->ops; + acos5_ops = *iso_ops; + + acos5_ops.match_card = acos5_match_card; + acos5_ops.init = acos5_init; + acos5_ops.finish = acos5_finish; + acos5_ops.select_file = acos5_select_file; + acos5_ops.card_ctl = acos5_card_ctl; + acos5_ops.list_files = acos5_list_files; + + return &acos5_drv; +} + +struct sc_card_driver *sc_get_acos5_driver(void) +{ + return sc_get_driver(); +} diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index c4bbab83..76c8e401 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -76,6 +76,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = { #ifdef HAVE_OPENSSL { "PIV-II",(void *) sc_get_piv_driver }, #endif + { "acos5", (void *(*)(void)) sc_get_acos5_driver }, /* The default driver should be last, as it handles all the * unrecognized cards. */ { "default", (void *(*)(void)) sc_get_default_driver }, diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h index b723f1c6..ef859565 100644 --- a/src/libopensc/opensc.h +++ b/src/libopensc/opensc.h @@ -1178,6 +1178,7 @@ extern sc_card_driver_t *sc_get_atrust_acos_driver(void); extern sc_card_driver_t *sc_get_incrypto34_driver(void); extern sc_card_driver_t *sc_get_piv_driver(void); extern sc_card_driver_t *sc_get_muscle_driver(void); +extern sc_card_driver_t *sc_get_acos5_driver(void); #ifdef __cplusplus }