diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index e46399c2..68df64d5 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -13,5 +13,5 @@ libopensc_la_SOURCES = asn1.c base64.c defaults.c \ libopensc_la_LDFLAGS = -version-info 0:4:0 libopensc_la_LIBADD = @LIBPCSC@ -include_HEADERS = opensc.h opensc-pkcs15.h +include_HEADERS = opensc.h opensc-pkcs15.h opensc-emv.h noinst_HEADERS = sc-asn1.h sc-log.h sc-internal.h diff --git a/src/libopensc/asn1.c b/src/libopensc/asn1.c index 418fcf4d..22c6bf46 100644 --- a/src/libopensc/asn1.c +++ b/src/libopensc/asn1.c @@ -1,5 +1,5 @@ /* - * sc-asn1.c: ASN.1 decoding functions (DER) + * asn1.c: ASN.1 decoding functions (DER) * * Copyright (C) 2001 Juha Yrjölä * diff --git a/src/libopensc/base64.c b/src/libopensc/base64.c index a8a886eb..0a4a3e9f 100644 --- a/src/libopensc/base64.c +++ b/src/libopensc/base64.c @@ -1,5 +1,5 @@ /* - * sc-base64.c: Base64 converting functions + * base64.c: Base64 converting functions * * Copyright (C) 2001 Juha Yrjölä * diff --git a/src/libopensc/card-default.c b/src/libopensc/card-default.c index 7535a8cc..d3e6f351 100644 --- a/src/libopensc/card-default.c +++ b/src/libopensc/card-default.c @@ -1,5 +1,5 @@ /* - * sc-card-unknown.c: Support for cards with no driver + * card-default.c: Support for cards with no driver * * Copyright (C) 2001 Juha Yrjölä * @@ -25,6 +25,7 @@ static struct sc_card_operations default_ops; static const struct sc_card_driver default_drv = { NULL, "Default driver for unknown cards", + "default", &default_ops }; @@ -43,6 +44,7 @@ static int autodetect_class(struct sc_card *card) int classes[] = { 0x00, 0xC0, 0xB0, 0xA0 }; int class_count = sizeof(classes)/sizeof(int); u8 buf[2]; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; struct sc_apdu apdu; int i, r; @@ -57,7 +59,8 @@ static int autodetect_class(struct sc_card *card) apdu.data = buf; apdu.datalen = 2; apdu.lc = 2; - apdu.resplen = 0; + apdu.resplen = sizeof(rbuf); + apdu.resp = rbuf; apdu.ins = 0xA4; apdu.p1 = apdu.p2 = 0; r = sc_transmit_apdu(card, &apdu); @@ -77,6 +80,25 @@ static int autodetect_class(struct sc_card *card) card->cla = classes[i]; if (card->ctx->debug >= 2) debug(card->ctx, "detected CLA byte as 0x%02X\n", card->cla); + if (apdu.resplen < 2) { + if (card->ctx->debug >= 2) + debug(card->ctx, "SELECT FILE returned %d bytes\n", + apdu.resplen); + return 0; + } + if (rbuf[0] == 0x6F) { + if (card->ctx->debug >= 2) + debug(card->ctx, "SELECT FILE seems to behave according to ISO 7816-4\n"); + return 0; + } + if (rbuf[0] == 0x00 && rbuf[1] == 0x00) { + const struct sc_card_driver *drv; + if (card->ctx->debug >= 2) + debug(card->ctx, "SELECT FILE seems to return Schlumberger 'flex stuff\n"); + drv = sc_get_mflex_driver(); + card->ops->select_file = drv->ops->select_file; + return 0; + } return 0; } diff --git a/src/libopensc/card-emv.c b/src/libopensc/card-emv.c index 5e1323e0..f0eba37f 100644 --- a/src/libopensc/card-emv.c +++ b/src/libopensc/card-emv.c @@ -1,5 +1,5 @@ /* - * sc-card-emv.c: Functions specified by the EMV standard + * card-emv.c: Functions specified by the EMV standard * * Copyright (C) 2001 Juha Yrjölä * @@ -25,6 +25,7 @@ static struct sc_card_operations emv_ops; static const struct sc_card_driver emv_drv = { NULL, "EMV compatible cards", + "emv", &emv_ops }; diff --git a/src/libopensc/card-multiflex.c b/src/libopensc/card-multiflex.c index d57cc60d..41dff9bd 100644 --- a/src/libopensc/card-multiflex.c +++ b/src/libopensc/card-multiflex.c @@ -1,5 +1,5 @@ /* - * sc-card-multiflex.c: Support for Multiflex cards by Schlumberger + * card-multiflex.c: Support for Multiflex cards by Schlumberger * * Copyright (C) 2001 Juha Yrjölä * @@ -22,6 +22,7 @@ #include "sc-log.h" static const char *mflex_atrs[] = { + "3B:95:94:40:FF:63:01:01:02:01", /* CryptoFlex 16k */ "3B:19:14:55:90:01:02:02:00:05:04:B0", NULL }; @@ -30,6 +31,7 @@ static struct sc_card_operations mflex_ops; static const struct sc_card_driver mflex_drv = { NULL, "Multiflex/Schlumberger", + "mflex", &mflex_ops }; @@ -70,13 +72,27 @@ static int mflex_init(struct sc_card *card) return 0; } +static unsigned int ac_to_acl(u8 nibble) +{ + unsigned int acl_table[16] = { + /* 0 */ SC_AC_NONE, SC_AC_CHV1, SC_AC_CHV2, SC_AC_PRO, + /* 4 */ SC_AC_AUT, SC_AC_UNKNOWN, SC_AC_CHV1 | SC_AC_PRO, + /* 7 */ SC_AC_CHV2 | SC_AC_PRO, SC_AC_CHV1 | SC_AC_AUT, + /* 9 */ SC_AC_CHV2 | SC_AC_AUT, SC_AC_UNKNOWN, SC_AC_UNKNOWN, + /* c */ SC_AC_UNKNOWN, SC_AC_UNKNOWN, SC_AC_UNKNOWN, + /* f */ SC_AC_NEVER }; + return acl_table[nibble & 0x0F]; +} + static int parse_flex_sf_reply(struct sc_context *ctx, const u8 *buf, int buflen, struct sc_file *file) { const u8 *p = buf + 2; u8 b1, b2; int left; - + + if (buflen < 14) + return -1; b1 = *p++; b2 = *p++; file->size = (b1 << 8) + b2; @@ -107,13 +123,39 @@ static int parse_flex_sf_reply(struct sc_context *ctx, const u8 *buf, int buflen error(ctx, "invalid file type: 0x%02X\n", *p); return SC_ERROR_UNKNOWN_REPLY; } - p++; + p += 2; + if (file->type == SC_FILE_TYPE_DF) { + file->acl[SC_AC_OP_LIST_FILES] = ac_to_acl(p[0] >> 4); + file->acl[SC_AC_OP_DELETE] = ac_to_acl(p[1] >> 4); + file->acl[SC_AC_OP_CREATE] = ac_to_acl(p[1] & 0x0F); + } else { /* EF */ + file->acl[SC_AC_OP_READ] = ac_to_acl(p[0] >> 4); + switch (file->ef_structure) { + case SC_FILE_EF_TRANSPARENT: + file->acl[SC_AC_OP_UPDATE] = ac_to_acl(p[0] & 0x0F); + break; + case SC_FILE_EF_LINEAR_FIXED: + case SC_FILE_EF_LINEAR_VARIABLE: + file->acl[SC_AC_OP_UPDATE] = ac_to_acl(p[0] & 0x0F); + break; + case SC_FILE_EF_CYCLIC: +#if 0 + /* FIXME */ + file->acl[SC_AC_OP_DECREASE] = ac_to_acl(p[0] & 0x0F); +#endif + break; + } + } + file->acl[SC_AC_OP_REHABILITATE] = ac_to_acl(p[2] >> 4); + file->acl[SC_AC_OP_INVALIDATE] = ac_to_acl(p[2] & 0x0F); p += 3; /* skip ACs */ if (*p++) file->status = SC_FILE_STATUS_ACTIVATED; else file->status = SC_FILE_STATUS_INVALIDATED; left = *p++; + /* FIXME: CODEME */ + file->magic = SC_FILE_MAGIC; return 0; } diff --git a/src/libopensc/card-setcos.c b/src/libopensc/card-setcos.c index 4a520cca..233df254 100644 --- a/src/libopensc/card-setcos.c +++ b/src/libopensc/card-setcos.c @@ -1,5 +1,5 @@ /* - * sc-card-setec.c: Support for PKI cards by Setec + * card-setec.c: Support for PKI cards by Setec * * Copyright (C) 2001 Juha Yrjölä * @@ -29,7 +29,8 @@ static const char *setec_atrs[] = { static struct sc_card_operations setec_ops; static const struct sc_card_driver setec_drv = { NULL, - "Setec", + "Setec smartcards", + "setec", &setec_ops }; diff --git a/src/libopensc/card.c b/src/libopensc/card.c index 8ba53014..c16dc52a 100644 --- a/src/libopensc/card.c +++ b/src/libopensc/card.c @@ -1,5 +1,5 @@ /* - * sc-card.c: General SmartCard functions + * card.c: General SmartCard functions * * Copyright (C) 2001 Juha Yrjölä * @@ -91,24 +91,36 @@ static int sc_check_apdu(struct sc_context *ctx, const struct sc_apdu *apdu) { switch (apdu->cse) { case SC_APDU_CASE_1: - if (apdu->datalen > 0) + if (apdu->datalen > 0) { + error(ctx, "Case 1 APDU with data supplied\n"); SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS); + } break; case SC_APDU_CASE_2_SHORT: - if (apdu->datalen > 0) + if (apdu->datalen > 0) { + error(ctx, "Case 2 APDU with data supplied\n"); SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS); - if (apdu->resplen < apdu->le) + } + if (apdu->resplen < apdu->le) { + error(ctx, "Response buffer size < Le\n"); SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS); + } break; case SC_APDU_CASE_3_SHORT: - if (apdu->datalen == 0 || apdu->data == NULL) + if (apdu->datalen == 0 || apdu->data == NULL) { + error(ctx, "Case 3 APDU with no data supplied\n"); SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS); + } break; case SC_APDU_CASE_4_SHORT: - if (apdu->datalen == 0 || apdu->data == NULL) + if (apdu->datalen == 0 || apdu->data == NULL) { + error(ctx, "Case 3 APDU with no data supplied\n"); SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS); - if (apdu->resplen < apdu->le) + } + if (apdu->resplen < apdu->le) { + error(ctx, "Le > response buffer size\n"); SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS); + } break; case SC_APDU_CASE_2_EXT: case SC_APDU_CASE_3_EXT: @@ -317,7 +329,7 @@ int sc_connect_card(struct sc_context *ctx, SCARDHANDLE card_handle; SCARD_READERSTATE_A rgReaderStates[SC_MAX_READERS]; LONG rv; - int i; + int i, r = 0; assert(card_out != NULL); SC_FUNC_CALLED(ctx, 1); @@ -339,13 +351,18 @@ int sc_connect_card(struct sc_context *ctx, if (card == NULL) SC_FUNC_RETURN(ctx, 1, SC_ERROR_OUT_OF_MEMORY); memset(card, 0, sizeof(struct sc_card)); + card->ops = malloc(sizeof(struct sc_card_operations)); + if (card->ops == NULL) { + free(card); + SC_FUNC_RETURN(ctx, 1, SC_ERROR_OUT_OF_MEMORY); + } rv = SCardConnect(ctx->pcsc_ctx, ctx->readers[reader], SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, &card_handle, &active_proto); if (rv != 0) { error(ctx, "SCardConnect failed: %s\n", pcsc_stringify_error(rv)); - free(card); - return -1; /* FIXME */ + r = -1; /* FIXME: invent a real error value */ + goto err; } card->reader = reader; card->ctx = ctx; @@ -356,8 +373,19 @@ int sc_connect_card(struct sc_context *ctx, i = SC_MAX_ATR_SIZE; memcpy(card->atr, rgReaderStates[0].rgbAtr, i); card->atr_len = i; - - for (i = 0; ctx->card_drivers[i] != NULL; i++) { + + if (ctx->default_driver != NULL) { + card->driver = ctx->default_driver; + memcpy(card->ops, card->driver->ops, sizeof(struct sc_card_operations)); + if (card->ops->init != NULL) { + r = card->ops->init(card); + if (r) { + error(ctx, "driver '%s' init() failed: %s\n", card->driver->name, + sc_strerror(r)); + goto err; + } + } + } else for (i = 0; ctx->card_drivers[i] != NULL; i++) { const struct sc_card_driver *drv = ctx->card_drivers[i]; const struct sc_card_operations *ops = drv->ops; int r; @@ -370,32 +398,34 @@ int sc_connect_card(struct sc_context *ctx, continue; if (ctx->debug >= 3) debug(ctx, "matched: %s\n", drv->name); - r = ops->init(card); - card->ops = ops; + memcpy(card->ops, ops, sizeof(struct sc_card_operations)); card->driver = drv; + r = ops->init(card); if (r) { error(ctx, "driver '%s' init() failed: %s\n", drv->name, sc_strerror(r)); if (r == SC_ERROR_INVALID_CARD) { - card->ops = NULL; card->driver = NULL; continue; } - free(card); - return r; + goto err; } break; } - if (card->ops == NULL) { + if (card->driver == NULL) { error(ctx, "unable to find driver for inserted card\n"); - free(card); - return SC_ERROR_INVALID_CARD; + r = SC_ERROR_INVALID_CARD; + goto err; } pthread_mutex_init(&card->mutex, NULL); card->magic = SC_CARD_MAGIC; *card_out = card; SC_FUNC_RETURN(ctx, 1, 0); +err: + free(card->ops); + free(card); + SC_FUNC_RETURN(ctx, 1, r); } int sc_disconnect_card(struct sc_card *card) @@ -413,6 +443,7 @@ int sc_disconnect_card(struct sc_card *card) } SCardDisconnect(card->pcsc_card, SCARD_LEAVE_CARD); pthread_mutex_destroy(&card->mutex); + free(card->ops); free(card); SC_FUNC_RETURN(ctx, 1, 0); } @@ -492,105 +523,48 @@ int sc_list_files(struct sc_card *card, u8 *buf, int buflen) return apdu.resplen; } -static int construct_fci(const struct sc_file *file, u8 *out, int *outlen) -{ - u8 *p = out; - u8 buf[32]; - - *p++ = 0x6F; - p++; - - buf[0] = (file->size >> 8) & 0xFF; - buf[1] = file->size & 0xFF; - sc_asn1_put_tag(0x81, buf, 2, p, 16, &p); - buf[0] = file->shareable ? 0x40 : 0; - buf[0] |= (file->type & 7) << 3; - buf[0] |= file->ef_structure & 7; - sc_asn1_put_tag(0x82, buf, 1, p, 16, &p); - buf[0] = (file->id >> 8) & 0xFF; - buf[1] = file->id & 0xFF; - sc_asn1_put_tag(0x83, buf, 2, p, 16, &p); - /* 0x84 = DF name */ - if (file->prop_attr_len) { - memcpy(buf, file->prop_attr, file->prop_attr_len); - sc_asn1_put_tag(0x85, buf, file->prop_attr_len, p, 18, &p); - } - if (file->sec_attr_len) { - memcpy(buf, file->sec_attr, file->sec_attr_len); - sc_asn1_put_tag(0x86, buf, file->sec_attr_len, p, 18, &p); - } - *p++ = 0xDE; - *p++ = 0; - *outlen = p - out; - out[1] = p - out - 2; - return 0; -} - int sc_create_file(struct sc_card *card, const struct sc_file *file) { - int r, len; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - struct sc_apdu apdu; + int r; + assert(card != NULL); SC_FUNC_CALLED(card->ctx, 1); - len = SC_MAX_APDU_BUFFER_SIZE; - r = construct_fci(file, sbuf, &len); - SC_TEST_RET(card->ctx, r, "construct_fci() failed"); - - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); - apdu.lc = len; - apdu.datalen = len; - apdu.data = sbuf; - apdu.resplen = sizeof(rbuf); - apdu.resp = rbuf; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, r, "APDU transmit failed"); - SC_FUNC_RETURN(card->ctx, 1, sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2)); + if (card->ops->create_file == NULL) + SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NOT_SUPPORTED); + r = card->ops->create_file(card, file); + SC_FUNC_RETURN(card->ctx, 1, r); } -int sc_delete_file(struct sc_card *card, int file_id) +int sc_delete_file(struct sc_card *card, const struct sc_path *path) { int r; - u8 sbuf[2]; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - struct sc_apdu apdu; + assert(card != NULL); SC_FUNC_CALLED(card->ctx, 1); - sbuf[0] = (file_id >> 8) & 0xFF; - sbuf[1] = file_id & 0xFF; - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); - apdu.lc = 2; - apdu.datalen = 2; - apdu.data = sbuf; - apdu.resplen = sizeof(rbuf); - apdu.resp = rbuf; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, r, "APDU transmit failed"); - if (apdu.resplen != 0) - SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_ILLEGAL_RESPONSE); - SC_FUNC_RETURN(card->ctx, 1, sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2)); + if (card->ops->delete_file == NULL) + SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NOT_SUPPORTED); + r = card->ops->delete_file(card, path); + SC_FUNC_RETURN(card->ctx, 1, r); } int sc_read_binary(struct sc_card *card, unsigned int idx, unsigned char *buf, size_t count, unsigned long flags) { -#define RB_BUF_SIZE 250 int r; assert(card != NULL && card->ops != NULL && buf != NULL); if (card->ctx->debug >= 2) debug(card->ctx, "sc_read_binary: %d bytes at index %d\n", count, idx); - if (count > RB_BUF_SIZE && !(card->caps & SC_CARD_CAP_APDU_EXT)) { + if (card->ops->read_binary == NULL) + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NOT_SUPPORTED); + if (count > SC_APDU_CHOP_SIZE && !(card->caps & SC_CARD_CAP_APDU_EXT)) { int bytes_read = 0; unsigned char *p = buf; r = sc_lock(card); SC_TEST_RET(card->ctx, r, "sc_lock() failed"); while (count > 0) { - int n = count > RB_BUF_SIZE ? RB_BUF_SIZE : count; + int n = count > SC_APDU_CHOP_SIZE ? SC_APDU_CHOP_SIZE : count; r = sc_read_binary(card, idx, p, n, flags); if (r < 0) { sc_unlock(card); @@ -608,11 +582,47 @@ int sc_read_binary(struct sc_card *card, unsigned int idx, sc_unlock(card); SC_FUNC_RETURN(card->ctx, 2, bytes_read); } - if (card->ops->read_binary == NULL) - SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NOT_SUPPORTED); r = card->ops->read_binary(card, idx, buf, count, flags); SC_FUNC_RETURN(card->ctx, 2, r); -#undef RB_BUF_SIZE +} + +int sc_write_binary(struct sc_card *card, unsigned int idx, + const u8 *buf, size_t count, unsigned long flags) +{ + int r; + + assert(card != NULL && card->ops != NULL && buf != NULL); + if (card->ctx->debug >= 2) + debug(card->ctx, "sc_write_binary: %d bytes at index %d\n", count, idx); + if (card->ops->write_binary == NULL) + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NOT_SUPPORTED); + if (count > SC_APDU_CHOP_SIZE && !(card->caps & SC_CARD_CAP_APDU_EXT)) { + int bytes_written = 0; + const u8 *p = buf; + + r = sc_lock(card); + SC_TEST_RET(card->ctx, r, "sc_lock() failed"); + while (count > 0) { + int n = count > SC_APDU_CHOP_SIZE ? SC_APDU_CHOP_SIZE : count; + r = sc_write_binary(card, idx, p, n, flags); + if (r < 0) { + sc_unlock(card); + SC_TEST_RET(card->ctx, r, "sc_read_binary() failed"); + } + p += r; + idx += r; + bytes_written += r; + count -= r; + if (r == 0) { + sc_unlock(card); + SC_FUNC_RETURN(card->ctx, 2, bytes_written); + } + } + sc_unlock(card); + SC_FUNC_RETURN(card->ctx, 2, bytes_written); + } + r = card->ops->write_binary(card, idx, buf, count, flags); + SC_FUNC_RETURN(card->ctx, 2, r); } int sc_select_file(struct sc_card *card, diff --git a/src/libopensc/defaults.c b/src/libopensc/defaults.c index bf2bffea..298baf02 100644 --- a/src/libopensc/defaults.c +++ b/src/libopensc/defaults.c @@ -1,5 +1,5 @@ /* - * sc-default.c: Card specific defaults + * default.c: Card specific defaults * * Copyright (C) 2001 Juha Yrjölä * diff --git a/src/libopensc/emv.c b/src/libopensc/emv.c new file mode 100644 index 00000000..a5b4ec7c --- /dev/null +++ b/src/libopensc/emv.c @@ -0,0 +1,23 @@ +/* + * emv.c: EMV functions + * + * Copyright (C) 2001 Juha Yrjölä + * + * 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 "opensc-emv.h" + +/* FIXME: Implement */ diff --git a/src/libopensc/emv.h b/src/libopensc/emv.h new file mode 100644 index 00000000..3de96df6 --- /dev/null +++ b/src/libopensc/emv.h @@ -0,0 +1,42 @@ +/* + * opensc-emv.h: OpenSC EMV header file + * + * Copyright (C) 2001 Juha Yrjölä + * + * 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 + */ + +#ifndef _OPENSC_EMV_H +#define _OPENSC_EMV_H + +#include "opensc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct sc_emv_card { + struct sc_card *card; +}; + +int sc_emv_bind(struct sc_card *card, struct sc_emv_card **emv_card); +int sc_emv_unbind(struct sc_emv_card *emv_card); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/libopensc/iso7816.c b/src/libopensc/iso7816.c index 588f128e..0d47b7b9 100644 --- a/src/libopensc/iso7816.c +++ b/src/libopensc/iso7816.c @@ -1,5 +1,5 @@ /* - * sc-iso7816-4.c: Functions specified by the ISO 7816-4 standard + * iso7816.c: Functions specified by the ISO 7816 standard * * Copyright (C) 2001 Juha Yrjölä * @@ -45,7 +45,7 @@ static int iso7816_read_binary(struct sc_card *card, SC_FUNC_RETURN(card->ctx, 2, sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2)); memcpy(buf, recvbuf, apdu.resplen); - SC_FUNC_RETURN(card->ctx, 2, apdu.resplen); + SC_FUNC_RETURN(card->ctx, 3, apdu.resplen); } static int iso7816_read_record(struct sc_card *card, @@ -71,7 +71,31 @@ static int iso7816_read_record(struct sc_card *card, SC_FUNC_RETURN(card->ctx, 2, sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2)); memcpy(buf, recvbuf, apdu.resplen); - SC_FUNC_RETURN(card->ctx, 2, apdu.resplen); + SC_FUNC_RETURN(card->ctx, 3, apdu.resplen); +} + +static int iso7816_write_binary(struct sc_card *card, + unsigned int idx, const u8 *buf, + size_t count, unsigned long flags) +{ + struct sc_apdu apdu; + int r; + + if (count > SC_APDU_CHOP_SIZE) { + error(card->ctx, "Too large buffer supplied\n"); + return SC_ERROR_CMD_TOO_LONG; + } + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD0, + (idx >> 8) & 0x7F, idx & 0xFF); + apdu.lc = count; + apdu.datalen = count; + apdu.data = buf; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2), + "Card returned error"); + SC_FUNC_RETURN(card->ctx, 3, count); } static unsigned int byte_to_acl(u8 byte) @@ -103,7 +127,7 @@ static void parse_sec_attr(struct sc_file *file, const u8 *buf, size_t len) } static void process_fci(struct sc_context *ctx, struct sc_file *file, - const u8 *buf, size_t buflen) + const u8 *buf, size_t buflen) { size_t taglen, len = buflen; const u8 *tag = NULL, *p = buf; @@ -306,12 +330,105 @@ static int iso7816_get_challenge(struct sc_card *card, u8 *rnd, size_t len) return 0; } +static int construct_fci(const struct sc_file *file, u8 *out, size_t *outlen) +{ + u8 *p = out; + u8 buf[32]; + + *p++ = 0x6F; + p++; + + buf[0] = (file->size >> 8) & 0xFF; + buf[1] = file->size & 0xFF; + sc_asn1_put_tag(0x81, buf, 2, p, 16, &p); + buf[0] = file->shareable ? 0x40 : 0; + switch (file->type) { + case SC_FILE_TYPE_WORKING_EF: + break; + case SC_FILE_TYPE_INTERNAL_EF: + buf[0] |= 0x08; + break; + case SC_FILE_TYPE_DF: + buf[0] |= 0x38; + break; + default: + return SC_ERROR_NOT_SUPPORTED; + } + buf[0] |= file->ef_structure & 7; + sc_asn1_put_tag(0x82, buf, 1, p, 16, &p); + buf[0] = (file->id >> 8) & 0xFF; + buf[1] = file->id & 0xFF; + sc_asn1_put_tag(0x83, buf, 2, p, 16, &p); + /* 0x84 = DF name */ + if (file->prop_attr_len) { + memcpy(buf, file->prop_attr, file->prop_attr_len); + sc_asn1_put_tag(0x85, buf, file->prop_attr_len, p, 18, &p); + } + if (file->sec_attr_len) { + memcpy(buf, file->sec_attr, file->sec_attr_len); + sc_asn1_put_tag(0x86, buf, file->sec_attr_len, p, 18, &p); + } +#if 0 + *p++ = 0xDE; /* what's this? */ + *p++ = 0; +#endif + out[1] = p - out - 2; + + *outlen = p - out; + return 0; +} + +static int iso7816_create_file(struct sc_card *card, const struct sc_file *file) +{ + int r, len; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + struct sc_apdu apdu; + + len = SC_MAX_APDU_BUFFER_SIZE; + r = construct_fci(file, sbuf, &len); + SC_TEST_RET(card->ctx, r, "construct_fci() failed"); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); + apdu.lc = len; + apdu.datalen = len; + apdu.data = sbuf; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + return sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2); +} + +static int iso7816_delete_file(struct sc_card *card, const struct sc_path *path) +{ + int r; + u8 sbuf[2]; + struct sc_apdu apdu; + + SC_FUNC_CALLED(card->ctx, 1); + if (path->type != SC_PATH_TYPE_FILE_ID && path->len != 2) { + error(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID\n"); + SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS); + } + sbuf[0] = path->value[0]; + sbuf[1] = path->value[1]; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); + apdu.lc = 2; + apdu.datalen = 2; + apdu.data = sbuf; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + return sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2); +} + static struct sc_card_operations iso_ops = { NULL, }; + static const struct sc_card_driver iso_driver = { NULL, - "ISO 7816-x reference driver", + "ISO 7816 reference driver", + "iso7816", &iso_ops }; @@ -324,11 +441,14 @@ const struct sc_card_driver * sc_get_iso7816_driver(void) { if (iso_ops.match_card == NULL) { memset(&iso_ops, 0, sizeof(iso_ops)); - iso_ops.match_card = no_match; - iso_ops.read_binary = iso7816_read_binary; - iso_ops.read_record = iso7816_read_record; - iso_ops.select_file = iso7816_select_file; + iso_ops.match_card = no_match; + iso_ops.read_binary = iso7816_read_binary; + iso_ops.read_record = iso7816_read_record; + iso_ops.write_binary = iso7816_write_binary; + iso_ops.select_file = iso7816_select_file; iso_ops.get_challenge = iso7816_get_challenge; + iso_ops.create_file = iso7816_create_file; + iso_ops.delete_file = iso7816_delete_file; } return &iso_driver; } diff --git a/src/libopensc/log.c b/src/libopensc/log.c index 09ecac17..4c458f42 100644 --- a/src/libopensc/log.c +++ b/src/libopensc/log.c @@ -1,5 +1,5 @@ /* - * sc-log.c: Miscellaneous logging functions + * log.c: Miscellaneous logging functions * * Copyright (C) 2001 Juha Yrjölä * diff --git a/src/libopensc/log.h b/src/libopensc/log.h index 08624fcb..8592c43c 100644 --- a/src/libopensc/log.h +++ b/src/libopensc/log.h @@ -53,7 +53,7 @@ void debug(struct sc_context *ctx, const char *format, ...); #define SC_TEST_RET(ctx, r, text) {\ int _ret = r;\ if (_ret < 0) {\ - error(ctx, text": %s\n", sc_strerror(r));\ + error(ctx, text": %s\n", sc_strerror(_ret));\ return _ret;\ }\ } diff --git a/src/libopensc/opensc-emv.h b/src/libopensc/opensc-emv.h new file mode 100644 index 00000000..3de96df6 --- /dev/null +++ b/src/libopensc/opensc-emv.h @@ -0,0 +1,42 @@ +/* + * opensc-emv.h: OpenSC EMV header file + * + * Copyright (C) 2001 Juha Yrjölä + * + * 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 + */ + +#ifndef _OPENSC_EMV_H +#define _OPENSC_EMV_H + +#include "opensc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct sc_emv_card { + struct sc_card *card; +}; + +int sc_emv_bind(struct sc_card *card, struct sc_emv_card **emv_card); +int sc_emv_unbind(struct sc_emv_card *emv_card); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/libopensc/opensc-pkcs15.h b/src/libopensc/opensc-pkcs15.h index e52f4e09..84afcd6a 100644 --- a/src/libopensc/opensc-pkcs15.h +++ b/src/libopensc/opensc-pkcs15.h @@ -180,7 +180,6 @@ struct sc_pkcs15_defaults { * a new PKCS#15 card object */ int sc_pkcs15_bind(struct sc_card *card, struct sc_pkcs15_card **pkcs15_card); - int sc_pkcs15_unbind(struct sc_pkcs15_card *card); int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card, diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h index a1ae7d89..18cb7136 100644 --- a/src/libopensc/opensc.h +++ b/src/libopensc/opensc.h @@ -104,8 +104,9 @@ extern "C" { #define SC_AC_NONE 0x00000000 #define SC_AC_CHV1 0x00000001 /* Card Holder Verif. */ #define SC_AC_CHV2 0x00000002 -#define SC_AC_TERM 0x00000004 /* Terminal auth */ -#define SC_AC_PRO 0x00000008 /* Protected mode */ +#define SC_AC_TERM 0x00000004 /* Terminal auth. */ +#define SC_AC_PRO 0x00000008 /* Secure Messaging */ +#define SC_AC_AUT 0x00000010 /* Key auth. */ #define SC_AC_NEVER 0xFFFFFFFE #define SC_AC_UNKNOWN 0xFFFFFFFF @@ -116,6 +117,7 @@ extern "C" { #define SC_AC_OP_CREATE 3 #define SC_AC_OP_REHABILITATE 4 #define SC_AC_OP_INVALIDATE 5 +#define SC_AC_OP_LIST_FILES 6 /* Operations relating to access control (in case of EF) */ #define SC_AC_OP_READ 0 @@ -124,15 +126,16 @@ extern "C" { #define SC_AC_OP_ERASE 3 /* rehab and invalidate are the same as in DF case */ -#define SC_MAX_AC_OPS 6 +#define SC_MAX_AC_OPS 7 -/* sc_read_binary() flags */ +/* sc_read_record() flags */ #define SC_READ_RECORD_EF_ID_MASK 0x0001F #define SC_READ_RECORD_BY_REC_ID 0x00000 #define SC_READ_RECORD_BY_REC_NR 0x00100 /* various maximum values */ #define SC_MAX_CARD_DRIVERS 16 +#define SC_MAX_CARD_DRIVER_SNAME_SIZE 16 #define SC_MAX_READERS 4 #define SC_MAX_APDU_BUFFER_SIZE 255 #define SC_MAX_PATH_SIZE 16 @@ -140,8 +143,8 @@ extern "C" { #define SC_MAX_ATR_SIZE 33 #define SC_MAX_SEC_ATTR_SIZE 16 #define SC_MAX_PROP_ATTR_SIZE 16 - -#define SC_MAX_OBJECT_ID_OCTETS 16 +#define SC_MAX_OBJECT_ID_OCTETS 16 +#define SC_APDU_CHOP_SIZE 250 typedef unsigned char u8; @@ -221,7 +224,7 @@ struct sc_card { pthread_mutex_t mutex; int lock_count; const struct sc_card_driver *driver; - const struct sc_card_operations *ops; + struct sc_card_operations *ops; void *ops_data; unsigned int magic; @@ -300,11 +303,17 @@ struct sc_card_operations { int (*reset_retry_counter)(struct sc_card *card, int ref_qualifier, const u8 *puk, size_t puklen, const u8 *newref, size_t newlen); + /* + * ISO 7816-9 functions + */ + int (*create_file)(struct sc_card *card, const struct sc_file *file); + int (*delete_file)(struct sc_card *card, const struct sc_path *path); }; struct sc_card_driver { char *libpath; /* NULL, if compiled in */ - char *name; + const char *name; + const char *short_name; struct sc_card_operations *ops; }; @@ -317,6 +326,7 @@ struct sc_context { int use_std_output, use_cache; const struct sc_card_driver *card_drivers[SC_MAX_CARD_DRIVERS+1]; + const struct sc_card_driver *default_driver; pthread_mutex_t mutex; }; @@ -345,6 +355,7 @@ void sc_format_apdu(struct sc_card *card, struct sc_apdu *apdu, int cse, int ins int sc_establish_context(struct sc_context **ctx); int sc_destroy_context(struct sc_context *ctx); +int sc_set_default_card_driver(struct sc_context *ctx, const char *short_name); int sc_connect_card(struct sc_context *ctx, int reader, struct sc_card **card); int sc_disconnect_card(struct sc_card *card); @@ -369,6 +380,8 @@ int sc_select_file(struct sc_card *card, const struct sc_path *path, struct sc_file *file); int sc_read_binary(struct sc_card *card, unsigned int idx, u8 * buf, size_t count, unsigned long flags); +int sc_write_binary(struct sc_card *card, unsigned int idx, const u8 * buf, + size_t count, unsigned long flags); int sc_read_record(struct sc_card *card, unsigned int rec_nr, u8 * buf, size_t count, unsigned long flags); int sc_get_challenge(struct sc_card *card, u8 * rndout, size_t len); @@ -391,7 +404,7 @@ int sc_reset_retry_counter(struct sc_card *card, int ref, const u8 *puk, /* ISO 7816-9 */ int sc_create_file(struct sc_card *card, const struct sc_file *file); -int sc_delete_file(struct sc_card *card, int file_id); +int sc_delete_file(struct sc_card *card, const struct sc_path *path); inline int sc_file_valid(const struct sc_file *file); void sc_format_path(const char *path_in, struct sc_path *path_out); diff --git a/src/libopensc/pkcs15-cert.c b/src/libopensc/pkcs15-cert.c index 95cfed70..bc8ed77e 100644 --- a/src/libopensc/pkcs15-cert.c +++ b/src/libopensc/pkcs15-cert.c @@ -1,5 +1,5 @@ /* - * sc-pkcs15-cert.c: PKCS#15 certificate functions + * pkcs15-cert.c: PKCS#15 certificate functions * * Copyright (C) 2001 Juha Yrjölä * diff --git a/src/libopensc/pkcs15-defaults.c b/src/libopensc/pkcs15-defaults.c index 18b705b2..d0a6dd1d 100644 --- a/src/libopensc/pkcs15-defaults.c +++ b/src/libopensc/pkcs15-defaults.c @@ -1,5 +1,5 @@ /* - * sc-default.c: Card specific defaults + * pkcs15-default.c: Obsolete * * Copyright (C) 2001 Juha Yrjölä * @@ -23,6 +23,7 @@ #include #include +#if 0 static void format_path(struct sc_path *path, const char *str) { int len = 0; @@ -166,3 +167,4 @@ const struct sc_pkcs15_defaults sc_pkcs15_card_table[] = { "07:2A:81:76:84:05:03:01", fineid_pkcs15_defaults, 2 }, { NULL, NULL, 0 } }; +#endif diff --git a/src/libopensc/pkcs15.c b/src/libopensc/pkcs15.c index 560013f9..5baecab5 100644 --- a/src/libopensc/pkcs15.c +++ b/src/libopensc/pkcs15.c @@ -153,6 +153,8 @@ static int parse_dir(const u8 * buf, size_t buflen, struct sc_pkcs15_card *card) card->label = strdup((char *) label); else card->label = strdup("(unknown)"); + if (path_len > SC_MAX_PATH_SIZE) + return -1; memcpy(card->file_app.path.value, path, path_len); card->file_app.path.len = path_len; card->file_app.path.type = SC_PATH_TYPE_PATH; @@ -220,28 +222,7 @@ static int parse_odf(const u8 * buf, int buflen, struct sc_pkcs15_card *card) static const struct sc_pkcs15_defaults * find_defaults(u8 *dir, int dirlen) { -#if 0 - int i = 0; - const struct sc_pkcs15_defaults *match = NULL; - - while (sc_card_table[i].atr != NULL) { - u8 defdir[128]; - int len = sizeof(defdir); - const struct sc_pkcs15_defaults *def = &sc_pkcs15_card_table[i]; - const char *dirp = def->ef_dir_dump; - i++; - - if (dirp == NULL) - break; - if (sc_hex_to_bin(dirp, defdir, &len)) - continue; - if (memcmp(dir, defdir, len) != 0) - continue; - match = def; - break; - } - return match; -#endif + /* FIXME: CODEME */ return NULL; } @@ -350,6 +331,19 @@ error: SC_FUNC_RETURN(ctx, 1, err); } +int sc_pkcs15_detect(struct sc_card *card) +{ + int r; + struct sc_path path; + struct sc_file file; + + sc_format_path("NA0000063504B43532D3135", &path); + r = sc_select_file(card, &path, &file); + if (r != 0) + return 0; + return 1; +} + int sc_pkcs15_unbind(struct sc_pkcs15_card *p15card) { assert(p15card != NULL); @@ -380,6 +374,6 @@ void sc_pkcs15_print_id(const struct sc_pkcs15_id *id) int sc_pkcs15_hex_string_to_id(const char *in, struct sc_pkcs15_id *out) { - out->len = sizeof(out->value); + out->len = sizeof(out->value); return sc_hex_to_bin(in, out->value, &out->len); } diff --git a/src/libopensc/pkcs15.h b/src/libopensc/pkcs15.h index e52f4e09..84afcd6a 100644 --- a/src/libopensc/pkcs15.h +++ b/src/libopensc/pkcs15.h @@ -180,7 +180,6 @@ struct sc_pkcs15_defaults { * a new PKCS#15 card object */ int sc_pkcs15_bind(struct sc_card *card, struct sc_pkcs15_card **pkcs15_card); - int sc_pkcs15_unbind(struct sc_pkcs15_card *card); int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card, diff --git a/src/libopensc/sc-log.h b/src/libopensc/sc-log.h index 08624fcb..8592c43c 100644 --- a/src/libopensc/sc-log.h +++ b/src/libopensc/sc-log.h @@ -53,7 +53,7 @@ void debug(struct sc_context *ctx, const char *format, ...); #define SC_TEST_RET(ctx, r, text) {\ int _ret = r;\ if (_ret < 0) {\ - error(ctx, text": %s\n", sc_strerror(r));\ + error(ctx, text": %s\n", sc_strerror(_ret));\ return _ret;\ }\ } diff --git a/src/libopensc/sc.c b/src/libopensc/sc.c index ed88aaf0..0963308d 100644 --- a/src/libopensc/sc.c +++ b/src/libopensc/sc.c @@ -146,22 +146,21 @@ int sc_establish_context(struct sc_context **ctx_out) ctx = malloc(sizeof(struct sc_context)); if (ctx == NULL) return SC_ERROR_OUT_OF_MEMORY; - ctx->use_std_output = 0; + memset(ctx, sizeof(struct sc_context), 0); ctx->use_cache = 1; - ctx->debug = 0; rv = SCardEstablishContext(SCARD_SCOPE_GLOBAL, "localhost", NULL, &ctx->pcsc_ctx); if (rv != SCARD_S_SUCCESS) return SC_ERROR_CONNECTING_TO_RES_MGR; SCardListReaders(ctx->pcsc_ctx, NULL, NULL, - (LPDWORD) & reader_buf_size); + (LPDWORD) &reader_buf_size); if (reader_buf_size < 2) { free(ctx); return SC_ERROR_NO_READERS_FOUND; } reader_buf = (char *) malloc(sizeof(char) * reader_buf_size); SCardListReaders(ctx->pcsc_ctx, mszGroups, reader_buf, - (LPDWORD) & reader_buf_size); + (LPDWORD) &reader_buf_size); p = reader_buf; ctx->reader_count = 0; do { @@ -209,6 +208,30 @@ int sc_destroy_context(struct sc_context *ctx) return 0; } +int sc_set_default_card_driver(struct sc_context *ctx, const char *short_name) +{ + int i = 0, match = 0; + + pthread_mutex_lock(&ctx->mutex); + if (short_name == NULL) { + ctx->default_driver = NULL; + match = 1; + } else while (ctx->card_drivers[i] != NULL && i < SC_MAX_CARD_DRIVERS) { + const struct sc_card_driver *drv = ctx->card_drivers[i]; + + if (strcmp(short_name, drv->short_name) == 0) { + ctx->default_driver = drv; + match = 1; + break; + } + i++; + } + pthread_mutex_unlock(&ctx->mutex); + if (match == 0) + return SC_ERROR_OBJECT_NOT_FOUND; /* FIXME: invent error */ + return 0; +} + void sc_format_path(const char *str, struct sc_path *path) { int len = 0; diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 951335e3..a39529b3 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -4,7 +4,7 @@ INCLUDES = @CFLAGS_PCSC@ @CFLAGS_OPENSC@ LDFLAGS = @LDFLAGS@ @LIBOPENSC@ noinst_PROGRAMS = base64 hst-test lottery p15dump \ - pintest prngtest + pintest prngtest filetest SRC = sc-test.c INC = sc-test.h @@ -15,3 +15,4 @@ lottery_SOURCES = lottery.c $(SRC) $(INC) p15dump_SOURCES = p15dump.c $(SRC) $(INC) pintest_SOURCES = pintest.c $(SRC) $(INC) prngtest_SOURCES = prngtest.c $(SRC) $(INC) +filetest_SOURCES = filetest.c $(SRC) $(INC) diff --git a/src/tests/filetest.c b/src/tests/filetest.c new file mode 100644 index 00000000..1c6a4712 --- /dev/null +++ b/src/tests/filetest.c @@ -0,0 +1,18 @@ +#include "sc-test.h" +#include "opensc.h" +#include +#include +#include + +int main(int argc, char *argv[]) +{ + int i; + struct sc_file file; + + i = sc_test_init(&argc, argv); + if (i < 0) + return 1; + memset(&file, 0, sizeof(file)); + + return 0; +} diff --git a/src/tests/hst-test.c b/src/tests/hst-test.c index ad0533d2..f98428f8 100644 --- a/src/tests/hst-test.c +++ b/src/tests/hst-test.c @@ -8,47 +8,29 @@ #include #include #include "sc-test.h" +#include "../libopensc/sc-log.h" struct sc_pkcs15_card *p15card; int test() { struct sc_file file; - struct sc_path path; struct sc_apdu apdu; + struct sc_path path; u8 rbuf[MAX_BUFFER_SIZE], sbuf[MAX_BUFFER_SIZE]; int r; sc_lock(card); - -#if 1 - r = sc_pkcs15_bind(card, &p15card); - if (r < 0) { - fprintf(stderr, "PKCS#15 init failed: %s\n", sc_strerror(r)); - goto err; - } - r = sc_pkcs15_enum_pins(p15card); - if (r < 0) { - fprintf(stderr, "PIN code enum failed: %s\n", sc_strerror(r)); - goto err; - } - sc_format_path("5110", &path); - ctx->debug = 3; + + sc_format_path("I3F00", &path); r = sc_select_file(card, &path, &file); - ctx->debug = 0; if (r) { - fprintf(stderr, "sc_select_file failed: %s\n", sc_strerror(r)); - goto err; + printf("SELECT FILE (MF) failed: %s\n", sc_strerror(r)); + return -1; } - r = sc_pkcs15_verify_pin(p15card, &p15card->pin_info[0], (const u8 *) "\x31\x32\x33\x34", 4); - if (r) { - fprintf(stderr, "PIN code verification failed: %s\n", sc_strerror(r)); - goto err; - } -#endif - ctx->debug = 3; - + ctx->debug = 5; +#if 1 sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0, 1); apdu.lc = 8; apdu.data = sbuf; @@ -61,27 +43,27 @@ int test() fprintf(stderr, "transmit failed: %s\n", sc_strerror(r)); goto err; } - r = sc_delete_file(card, 0x5110); + sc_format_path("I1234", &path); + r = sc_delete_file(card, &path); if (r) { fprintf(stderr, "fail: %s\n", sc_strerror(r)); goto err; } return 0; - +#endif memset(&file, 0, sizeof(file)); - file.id = 0x5110; + file.id = 0x1234; file.sec_attr_len = 6; memcpy(file.sec_attr, "\x00\x00\x00\x00\x00\x00", 6); file.prop_attr_len = 3; - memcpy(file.prop_attr, "\x23\x00\x00", 3); + memcpy(file.prop_attr, "\x03\x00\x00", 3); file.size = 32; file.type = SC_FILE_TYPE_WORKING_EF; file.ef_structure = SC_FILE_EF_TRANSPARENT; - ctx->debug = 1; + ctx->debug = 5; r = sc_create_file(card, &file); - err: sc_unlock(card); return r; @@ -92,15 +74,35 @@ int test2() int r; struct sc_path path; struct sc_file file; + u8 buf[32]; + u8 output[1024]; + int i; - sc_format_path("3F00", &path); + sc_format_path("1234", &path); - ctx->debug = 3; + ctx->debug = 5; r = sc_select_file(card, &path, &file); if (r) { fprintf(stderr, "SELECT FILE failed: %s\n", sc_strerror(r)); return r; } + for (i = 0; i < sizeof(buf); i++) + buf[i] = i; + r = sc_write_binary(card, 0, buf, sizeof(buf), 0); + if (r < 0) { + fprintf(stderr, "WRITE BINARY failed: %s\n", sc_strerror(r)); + return r; + } else + printf("%d bytes written.\n", r); + memset(buf, 0, sizeof(buf)); + r = sc_read_binary(card, 0, buf, sizeof(buf), 0); + if (r < 0) { + fprintf(stderr, "READ BINARY failed: %s\n", sc_strerror(r)); + return r; + } else + printf("%d bytes read.\n", r); + sc_hex_dump(ctx, buf, r, output, sizeof(output)); + printf("%s", output); return 0; } @@ -156,7 +158,7 @@ int main(int argc, char **argv) return 1; ctx->debug = 3; - if (test3()) + if (test()) return 1; sc_test_cleanup(); diff --git a/src/tests/sc-test.c b/src/tests/sc-test.c index 4878864e..4478671c 100644 --- a/src/tests/sc-test.c +++ b/src/tests/sc-test.c @@ -16,12 +16,13 @@ int sc_test_init(int *argc, char *argv[]) { int i, c; - printf("Using libsc version %s.\n", sc_version); + printf("Using libopensc version %s.\n", sc_version); i = sc_establish_context(&ctx); if (i < 0) { printf("sc_establish_context() failed (%d)\n", i); return i; } + ctx->use_std_output = 1; i = sc_detect_card(ctx, 0); printf("Card %s.\n", i == 1 ? "present" : "absent"); if (i < 0) { diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am index cec51f76..6e5fe837 100644 --- a/src/tools/Makefile.am +++ b/src/tools/Makefile.am @@ -3,10 +3,12 @@ INCLUDES = @CFLAGS_PCSC@ @CFLAGS_OPENSC@ LDFLAGS = @LDFLAGS@ @LIBOPENSC@ -bin_PROGRAMS = opensc-crypt opensc-tool +bin_PROGRAMS = opensc-tool pkcs15-crypt pkcs15-tool -opensc_crypt_SOURCES = opensc-crypt.c util.c -opensc_crypt_LDADD = @GETOPTSRC@ opensc_tool_SOURCES = opensc-tool.c util.c opensc_tool_LDADD = @GETOPTSRC@ +pkcs15_tool_SOURCES = pkcs15-tool.c util.c +pkcs15_tool_LDADD = @GETOPTSRC@ +pkcs15_crypt_SOURCES = pkcs15-crypt.c util.c +pkcs15_crypt_LDADD = @GETOPTSRC@ noinst_HEADERS = util.h diff --git a/src/tools/opensc-tool.c b/src/tools/opensc-tool.c index a904e7aa..b878b38c 100644 --- a/src/tools/opensc-tool.c +++ b/src/tools/opensc-tool.c @@ -18,25 +18,17 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifdef HAVE_CONFIG_H -#include -#endif +#include "util.h" #include #include #include -#ifdef HAVE_GETOPT_H -#include -#endif #include #include #include #include -#include #include #include -#include "util.h" - #define OPT_CHANGE_PIN 0x100 #define OPT_LIST_PINS 0x101 #define OPT_READER 0x102 @@ -44,30 +36,19 @@ #define OPT_NO_CACHE 0x104 int opt_reader = 0, opt_no_cache = 0, opt_debug = 0; -char * opt_pin_id; -char * opt_cert = NULL; -char * opt_outfile = NULL; -char * opt_newpin = NULL; -char * opt_apdu = NULL; +char * opt_apdus[8]; +int opt_apdu_count = 0; int quiet = 0; const struct option options[] = { { "list-readers", 0, 0, 'l' }, { "list-drivers", 0, 0, 'D' }, { "list-files", 0, 0, 'f' }, - { "learn-card", 0, 0, 'L' }, { "send-apdu", 1, 0, 's' }, - { "read-certificate", 1, 0, 'r' }, - { "list-certificates", 0, 0, 'c' }, - { "list-pins", 0, 0, OPT_LIST_PINS }, - { "change-pin", 0, 0, OPT_CHANGE_PIN }, - { "list-private-keys", 0, 0, 'k' }, - { "reader", 1, 0, OPT_READER }, - { "output", 1, 0, 'o' }, + { "reader", 1, 0, 'r' }, + { "card-driver", 1, 0, 'c' }, { "quiet", 0, 0, 'q' }, { "debug", 0, 0, 'd' }, - { "no-cache", 0, 0, OPT_NO_CACHE }, - { "pin-id", 1, 0, 'p' }, { 0, 0, 0, 0 } }; @@ -75,55 +56,15 @@ const char *option_help[] = { "Lists all configured readers", "Lists all installed card drivers", "Recursively lists files stored on card", - "Stores card info to cache [P15]", "Sends an APDU in format AA:BB:CC:DD:EE:FF...", - "Reads certificate with ID [P15]", - "Lists certificates [P15]", - "Lists PIN codes [P15]", - "Changes the PIN code [P15]", - "Lists private keys [P15]", - "Uses reader number ", - "Outputs to file ", + "Uses reader number [0]", + "Forces the use of driver [auto-detect]", "Quiet operation", "Debug output -- may be supplied several times", - "Disable card caching", - "The auth ID of the PIN to use [P15]", }; struct sc_context *ctx = NULL; struct sc_card *card = NULL; -struct sc_pkcs15_card *p15card = NULL; - -void print_usage_and_die(void) -{ - int i = 0; - printf("Usage: opensc-tool [OPTIONS]\nOptions:\n"); - - while (options[i].name) { - char buf[40], tmp[5]; - const char *arg_str; - - if (options[i].val > 0 && options[i].val < 128) - sprintf(tmp, ", -%c", options[i].val); - else - tmp[0] = 0; - switch (options[i].has_arg) { - case 1: - arg_str = " "; - break; - case 2: - arg_str = " [arg]"; - break; - default: - arg_str = ""; - break; - } - sprintf(buf, "--%s%s%s", options[i].name, tmp, arg_str); - printf(" %-30s%s\n", buf, option_help[i]); - i++; - } - exit(2); -} int list_readers(void) { @@ -135,7 +76,7 @@ int list_readers(void) } printf("Configured readers:\n"); for (i = 0; i < ctx->reader_count; i++) { - printf("\t%d - %s\n", i, ctx->readers[i]); + printf(" %d - %s\n", i, ctx->readers[i]); } return 0; } @@ -150,228 +91,12 @@ int list_drivers(void) } printf("Configured card drivers:\n"); for (i = 0; ctx->card_drivers[i] != NULL; i++) { - printf("\t%s\n", ctx->card_drivers[i]->name); + printf(" %-16s %s\n", ctx->card_drivers[i]->short_name, + ctx->card_drivers[i]->name); } return 0; } -int list_certificates(void) -{ - int r, i; - - r = sc_pkcs15_enum_certificates(p15card); - if (r < 0) { - fprintf(stderr, "Certificate enumeration failed: %s\n", sc_strerror(r)); - return 1; - } - if (!quiet) - printf("Card has %d certificate(s).\n\n", p15card->cert_count); - for (i = 0; i < p15card->cert_count; i++) { - struct sc_pkcs15_cert_info *cinfo = &p15card->cert_info[i]; - sc_pkcs15_print_cert_info(cinfo); - printf("\n"); - } - return 0; -} - -int print_pem_certificate(struct sc_pkcs15_cert *cert) -{ - int r; - u8 buf[2048]; - FILE *outf; - - r = sc_base64_encode(cert->data, cert->data_len, buf, - sizeof(buf), 64); - if (r) { - fprintf(stderr, "Base64 encoding failed: %s\n", sc_strerror(r)); - return 1; - } - if (opt_outfile != NULL) { - outf = fopen(opt_outfile, "w"); - if (outf == NULL) { - fprintf(stderr, "Error opening file '%s': %s\n", - opt_outfile, strerror(errno)); - return 2; - } - } else - outf = stdout; - fprintf(outf, "-----BEGIN CERTIFICATE-----\n%s-----END CERTIFICATE-----\n", - buf); - if (outf != stdout) - fclose(outf); - return 0; -} - -int read_certificate(void) -{ - int r, i; - struct sc_pkcs15_id id; - - id.len = SC_PKCS15_MAX_ID_SIZE; - sc_pkcs15_hex_string_to_id(opt_cert, &id); - - r = sc_pkcs15_enum_certificates(p15card); - if (r < 0) { - fprintf(stderr, "Certificate enumeration failed: %s\n", sc_strerror(r)); - return 1; - } - - for (i = 0; i < p15card->cert_count; i++) { - struct sc_pkcs15_cert_info *cinfo = &p15card->cert_info[i]; - struct sc_pkcs15_cert *cert; - - if (sc_pkcs15_compare_id(&id, &cinfo->id) != 1) - continue; - - if (!quiet) - printf("Reading certificate with ID '%s'\n", opt_cert); - r = sc_pkcs15_read_certificate(p15card, cinfo, &cert); - if (r) { - fprintf(stderr, "Certificate read failed: %s\n", sc_strerror(r)); - return 1; - } - r = print_pem_certificate(cert); - sc_pkcs15_free_certificate(cert); - return r; - } - fprintf(stderr, "Certificate with ID '%s' not found.\n", opt_cert); - return 2; -} - -int list_private_keys(void) -{ - int r, i; - - r = sc_pkcs15_enum_private_keys(p15card); - if (r < 0) { - fprintf(stderr, "Private key enumeration failed: %s\n", sc_strerror(r)); - return 1; - } - if (!quiet) - printf("Card has %d private key(s).\n\n", p15card->prkey_count); - for (i = 0; i < p15card->prkey_count; i++) { - struct sc_pkcs15_prkey_info *pinfo = &p15card->prkey_info[i]; - sc_pkcs15_print_prkey_info(pinfo); - printf("\n"); - } - return 0; -} - -u8 * get_pin(const char *prompt, struct sc_pkcs15_pin_info **pin_out) -{ - int r; - char buf[80]; - char *pincode; - struct sc_pkcs15_pin_info *pinfo; - - if (pin_out != NULL) - pinfo = *pin_out; - - if (pinfo == NULL && opt_pin_id == NULL) { - r = sc_pkcs15_enum_pins(p15card); - if (r < 0) { - fprintf(stderr, "PIN code enumeration failed: %s\n", sc_strerror(r)); - return NULL; - } - if (r == 0) { - fprintf(stderr, "No PIN codes found.\n"); - return NULL; - } - pinfo = &p15card->pin_info[0]; - } else if (pinfo == NULL) { - struct sc_pkcs15_id pin_id; - - sc_pkcs15_hex_string_to_id(opt_pin_id, &pin_id); - r = sc_pkcs15_find_pin_by_auth_id(p15card, &pin_id, &pinfo); - if (r) { - fprintf(stderr, "Unable to find PIN code: %s\n", sc_strerror(r)); - return NULL; - } - } - - if (pin_out != NULL) - *pin_out = pinfo; - - sprintf(buf, "%s [%s]: ", prompt, pinfo->com_attr.label); - while (1) { - pincode = getpass(buf); - if (strlen(pincode) == 0) - return NULL; - if (strlen(pincode) < pinfo->min_length) { - printf("PIN code too short, try again.\n"); - continue; - } - if (strlen(pincode) > pinfo->stored_length) { - printf("PIN code too long, try again.\n"); - continue; - } - return (u8 *) strdup(pincode); - } -} - -int list_pins(void) -{ - int r, i; - - r = sc_pkcs15_enum_pins(p15card); - if (r < 0) { - fprintf(stderr, "PIN code enumeration failed: %s\n", sc_strerror(r)); - return 1; - } - if (!quiet) - printf("Card has %d PIN code(s).\n\n", p15card->pin_count); - for (i = 0; i < p15card->pin_count; i++) { - struct sc_pkcs15_pin_info *pinfo = &p15card->pin_info[i]; - sc_pkcs15_print_pin_info(pinfo); - printf("\n"); - } - return 0; -} - -int change_pin(void) -{ - struct sc_pkcs15_pin_info *pinfo = NULL; - u8 *pincode, *newpin; - int r; - - pincode = get_pin("Enter old PIN", &pinfo); - if (pincode == NULL) - return 2; - if (strlen((char *) pincode) == 0) { - fprintf(stderr, "No PIN code supplied.\n"); - return 2; - } - while (1) { - u8 *newpin2; - - newpin = get_pin("Enter new PIN", &pinfo); - if (newpin == NULL || strlen((char *) newpin) == 0) - return 2; - newpin2 = get_pin("Enter new PIN again", &pinfo); - if (newpin2 == NULL || strlen((char *) newpin2) == 0) - return 2; - if (strcmp((char *) newpin, (char *) newpin2) == 0) { - free(newpin2); - break; - } - printf("PIN codes do not match, try again.\n"); - free(newpin); - free(newpin2); - } - r = sc_pkcs15_change_pin(p15card, pinfo, pincode, strlen((char *) pincode), - newpin, strlen((char *) newpin)); - if (r == SC_ERROR_PIN_CODE_INCORRECT) { - fprintf(stderr, "PIN code incorrect; tries left: %d\n", pinfo->tries_left); - return 3; - } else if (r) { - fprintf(stderr, "PIN code change failed: %s\n", sc_strerror(r)); - return 2; - } - if (!quiet) - printf("PIN code changed successfully.\n"); - return 0; -} - const char * print_acl(unsigned int acl) { static char line[80]; @@ -391,6 +116,9 @@ const char * print_acl(unsigned int acl) strcat(line, "TERM "); if (acl & SC_AC_PRO) strcat(line, "PROT "); + if (acl & SC_AC_AUT) + strcat(line, "AUTH "); + line[strlen(line)-1] = 0; /* get rid of trailing space */ return line; } @@ -400,7 +128,8 @@ int print_file(struct sc_card *card, const struct sc_file *file, const struct sc int r; const char *tmps; const char *ac_ops_df[] = { - "select", "lock", "delete", "create", "rehab", "inval" + "select", "lock", "delete", "create", "rehab", "inval", + "list" }; const char *ac_ops_ef[] = { "read", "update", "write", "erase", "rehab", "inval" @@ -528,158 +257,80 @@ int list_files(void) return r; } -static int generate_cert_filename(struct sc_pkcs15_card *p15card, - const struct sc_pkcs15_cert_info *info, - char *fname, int len) -{ - char *homedir; - char cert_id[SC_PKCS15_MAX_ID_SIZE*2+1]; - int i, r; - - homedir = getenv("HOME"); - if (homedir == NULL) - return -1; - cert_id[0] = 0; - for (i = 0; i < info->id.len; i++) { - char tmp[3]; - - sprintf(tmp, "%02X", info->id.value[i]); - strcat(cert_id, tmp); - } - r = snprintf(fname, len, "%s/%s/%s_%s_%s.crt", homedir, - SC_PKCS15_CACHE_DIR, p15card->label, - p15card->serial_number, cert_id); - if (r < 0) - return -1; - return 0; -} - -int learn_card(void) -{ - struct stat stbuf; - char fname[512], *home; - int r, i; - - home = getenv("HOME"); - if (home == NULL) { - fprintf(stderr, "No $HOME environment variable set.\n"); - return 1; - } - sprintf(fname, "%s/%s", home, SC_PKCS15_CACHE_DIR); - r = stat(fname, &stbuf); - if (r) { - printf("No '%s' directory found, creating...\n", fname); - r = mkdir(fname, 0700); - if (r) { - perror("Directory creation failed"); - return 1; - } - } - printf("Using cache directory '%s'.\n", fname); - r = sc_pkcs15_enum_certificates(p15card); - if (r < 0) { - fprintf(stderr, "Certificate enumeration failed: %s\n", sc_strerror(r)); - return 1; - } - printf("Caching %d certificate(s)...\n", r); - p15card->use_cache = 0; - for (i = 0; i < p15card->cert_count; i++) { - struct sc_pkcs15_cert_info *cinfo = &p15card->cert_info[i]; - struct sc_pkcs15_cert *cert; - FILE *crtf; - - printf("Reading certificate: %s...\n", cinfo->com_attr.label); - r = sc_pkcs15_read_certificate(p15card, cinfo, &cert); - if (r) { - fprintf(stderr, "Certificate read failed: %s\n", sc_strerror(r)); - return 1; - } - r = generate_cert_filename(p15card, cinfo, fname, sizeof(fname)); - if (r) - return 1; - crtf = fopen(fname, "w"); - if (crtf == NULL) { - perror(fname); - return 1; - } - fwrite(cert->data, cert->data_len, 1, crtf); - fclose(crtf); - - sc_pkcs15_free_certificate(cert); - } - - return 0; -} - int send_apdu(void) { struct sc_apdu apdu; u8 buf[MAX_BUFFER_SIZE], sbuf[MAX_BUFFER_SIZE], - rbuf[MAX_BUFFER_SIZE], *p = buf; - size_t len, len0 = sizeof(buf), r; - - sc_hex_to_bin(opt_apdu, buf, &len0); - if (len0 < 4) { - fprintf(stderr, "APDU too short (must be at least 4 bytes).\n"); - return 2; - } - len = len0; - apdu.cla = *p++; - apdu.ins = *p++; - apdu.p1 = *p++; - apdu.p2 = *p++; - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); - apdu.data = NULL; - apdu.datalen = 0; - len -= 4; - if (len > 1) { - apdu.lc = *p++; - len--; - memcpy(sbuf, p, apdu.lc); - apdu.data = sbuf; - apdu.datalen = apdu.lc; - if (len < apdu.lc) { - fprintf(stderr, "APDU too short (need %d bytes).\n", - apdu.lc-len); + rbuf[MAX_BUFFER_SIZE], *p; + size_t len, len0, r; + int c; + + for (c = 0; c < opt_apdu_count; c++) { + len0 = sizeof(buf); + sc_hex_to_bin(opt_apdus[c], buf, &len0); + if (len0 < 4) { + fprintf(stderr, "APDU too short (must be at least 4 bytes).\n"); return 2; } - len -= apdu.lc; - if (len) { + len = len0; + p = buf; + apdu.cla = *p++; + apdu.ins = *p++; + apdu.p1 = *p++; + apdu.p2 = *p++; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.data = NULL; + apdu.datalen = 0; + len -= 4; + if (len > 1) { + apdu.lc = *p++; + len--; + memcpy(sbuf, p, apdu.lc); + apdu.data = sbuf; + apdu.datalen = apdu.lc; + if (len < apdu.lc) { + fprintf(stderr, "APDU too short (need %d bytes).\n", + apdu.lc-len); + return 2; + } + len -= apdu.lc; + if (len) { + apdu.le = *p++; + len--; + apdu.cse = SC_APDU_CASE_4_SHORT; + } else + apdu.cse = SC_APDU_CASE_3_SHORT; + if (len) { + fprintf(stderr, "APDU too long (%d bytes extra).\n", len); + return 2; + } + } else if (len == 1) { apdu.le = *p++; len--; - apdu.cse = SC_APDU_CASE_4_SHORT; + apdu.cse = SC_APDU_CASE_2_SHORT; } else - apdu.cse = SC_APDU_CASE_3_SHORT; - if (len) { - fprintf(stderr, "APDU too long (%d bytes extra).\n", len); - return 2; + apdu.cse = SC_APDU_CASE_1; + printf("Sending: "); + for (r = 0; r < len0; r++) + printf("%02X ", buf[r]); + printf("\n"); + #if 0 + ctx->debug = 5; + #endif + r = sc_transmit_apdu(card, &apdu); + #if 0 + ctx->debug = opt_debug; + #endif + if (r) { + fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); + return 1; } - } else if (len == 1) { - apdu.le = *p++; - len--; - apdu.cse = SC_APDU_CASE_2_SHORT; - } else - apdu.cse = SC_APDU_CASE_1; - printf("Sending: "); - for (r = 0; r < len0; r++) - printf("%02X ", buf[r]); - printf("\n"); -#if 0 - ctx->debug = 5; -#endif - r = sc_transmit_apdu(card, &apdu); -#if 0 - ctx->debug = opt_debug; -#endif - if (r) { - fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); - return 1; + printf("Received (SW1=0x%02X, SW2=0x%02X)%s\n", apdu.sw1, apdu.sw2, + apdu.resplen ? ":" : ""); + if (apdu.resplen) + hex_dump_asc(stdout, apdu.resp, apdu.resplen); } - printf("Received (SW1=0x%02X, SW2=0x%02X)%s\n", apdu.sw1, apdu.sw2, - apdu.resplen ? ":" : ""); - if (apdu.resplen) - hex_dump_asc(stdout, apdu.resp, apdu.resplen); return 0; } @@ -688,28 +339,18 @@ int main(int argc, char * const argv[]) int err = 0, r, c, long_optind = 0; int do_list_readers = 0; int do_list_drivers = 0; - int do_read_cert = 0; - int do_list_certs = 0; - int do_list_pins = 0; int do_list_files = 0; - int do_list_prkeys = 0; - int do_change_pin = 0; int do_send_apdu = 0; - int do_learn_card = 0; int action_count = 0; + const char *opt_driver = NULL; while (1) { - c = getopt_long(argc, argv, "lfr:kco:qdp:s:LD", options, &long_optind); + c = getopt_long(argc, argv, "lfr:qds:Dc:", options, &long_optind); if (c == -1) break; if (c == '?') - print_usage_and_die(); + print_usage_and_die("opensc-tool"); switch (c) { - case 'r': - opt_cert = optarg; - do_read_cert = 1; - action_count++; - break; case 'l': do_list_readers = 1; action_count++; @@ -722,53 +363,29 @@ int main(int argc, char * const argv[]) do_list_files = 1; action_count++; break; - case 'c': - do_list_certs = 1; - action_count++; - break; case 's': - opt_apdu = optarg; + opt_apdus[opt_apdu_count] = optarg; do_send_apdu++; - action_count++; + if (opt_apdu_count == 0) + action_count++; + opt_apdu_count++; break; - case OPT_CHANGE_PIN: - do_change_pin = 1; - action_count++; - break; - case OPT_LIST_PINS: - do_list_pins = 1; - action_count++; - break; - case 'k': - do_list_prkeys = 1; - action_count++; - break; - case 'L': - do_learn_card = 1; - action_count++; - break; - case OPT_READER: + case 'r': opt_reader = atoi(optarg); break; - case 'o': - opt_outfile = optarg; - break; case 'q': quiet++; break; case 'd': opt_debug++; break; - case 'p': - opt_pin_id = optarg; - break; - case OPT_NO_CACHE: - opt_no_cache++; + case 'c': + opt_driver = optarg; break; } } if (action_count == 0) - print_usage_and_die(); + print_usage_and_die("opensc-tool"); r = sc_establish_context(&ctx); if (r) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); @@ -797,7 +414,16 @@ int main(int argc, char * const argv[]) } if (sc_detect_card(ctx, opt_reader) != 1) { fprintf(stderr, "Card not present.\n"); - return 3; + err = 3; + goto end; + } + if (opt_driver != NULL) { + err = sc_set_default_card_driver(ctx, opt_driver); + if (err) { + fprintf(stderr, "Driver '%s' not found!\n", opt_driver); + err = 1; + goto end; + } } if (!quiet) fprintf(stderr, "Connecting to card in reader %s...\n", ctx->readers[opt_reader]); @@ -826,52 +452,7 @@ int main(int argc, char * const argv[]) goto end; action_count--; } - - if (action_count <= 0) - goto end; - if (!quiet) - fprintf(stderr, "Trying to find a PKCS#15 compatible card...\n"); - r = sc_pkcs15_bind(card, &p15card); - if (r) { - fprintf(stderr, "PKCS#15 initialization failed: %s\n", sc_strerror(r)); - err = 1; - goto end; - } - if (!quiet) - fprintf(stderr, "Found %s!\n", p15card->label); - if (do_learn_card) { - if ((err = learn_card())) - goto end; - action_count--; - } - if (do_list_certs) { - if ((err = list_certificates())) - goto end; - action_count--; - } - if (do_read_cert) { - if ((err = read_certificate())) - goto end; - action_count--; - } - if (do_list_prkeys) { - if ((err = list_private_keys())) - goto end; - action_count--; - } - if (do_list_pins) { - if ((err = list_pins())) - goto end; - action_count--; - } - if (do_change_pin) { - if ((err = change_pin())) - goto end; - action_count--; - } end: - if (p15card) - sc_pkcs15_unbind(p15card); if (card) { sc_unlock(card); sc_disconnect_card(card); diff --git a/src/tools/opensc-crypt.c b/src/tools/pkcs15-crypt.c similarity index 91% rename from src/tools/opensc-crypt.c rename to src/tools/pkcs15-crypt.c index 86442811..c85de360 100644 --- a/src/tools/opensc-crypt.c +++ b/src/tools/pkcs15-crypt.c @@ -18,23 +18,16 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifdef HAVE_CONFIG_H -#include -#endif +#include "util.h" #include #include #include -#ifdef HAVE_GETOPT_H -#include -#endif #include #include #include #include #include -#include "util.h" - int opt_reader = 0, opt_pin = 0, quiet = 0; int opt_debug = 0; char * opt_pincode = NULL, * opt_key_id = NULL; @@ -69,44 +62,13 @@ const char *option_help[] = { "Quiet operation", "Debug output -- may be supplied several times", "Uses password (PIN) ", - "The auth ID of the PIN to use [P15]", + "The auth ID of the PIN to use", }; struct sc_context *ctx = NULL; struct sc_card *card = NULL; struct sc_pkcs15_card *p15card = NULL; -void print_usage_and_die() -{ - int i = 0; - printf("Usage: opensc-crypt [OPTIONS]\nOptions:\n"); - - while (options[i].name) { - char buf[40], tmp[5]; - const char *arg_str; - - if (options[i].val > 0 && options[i].val < 128) - sprintf(tmp, ", -%c", options[i].val); - else - tmp[0] = 0; - switch (options[i].has_arg) { - case 1: - arg_str = " "; - break; - case 2: - arg_str = " [arg]"; - break; - default: - arg_str = ""; - break; - } - sprintf(buf, "--%s%s%s", options[i].name, tmp, arg_str); - printf(" %-30s%s\n", buf, option_help[i]); - i++; - } - exit(2); -} - char * get_pin(struct sc_pkcs15_pin_info *pinfo) { char buf[80]; @@ -236,7 +198,7 @@ int main(int argc, char * const argv[]) if (c == -1) break; if (c == '?') - print_usage_and_die(); + print_usage_and_die("opensc-crypt"); switch (c) { case 's': do_sign++; @@ -274,7 +236,7 @@ int main(int argc, char * const argv[]) } } if (action_count == 0) - print_usage_and_die(); + print_usage_and_die("opensc-crypt"); r = sc_establish_context(&ctx); if (r) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); diff --git a/src/tools/pkcs15-tool.c b/src/tools/pkcs15-tool.c new file mode 100644 index 00000000..28cf999d --- /dev/null +++ b/src/tools/pkcs15-tool.c @@ -0,0 +1,503 @@ + +#include "util.h" +#include + +int opt_reader = 0, opt_debug = 0; +int opt_no_cache = 0; +char * opt_pin_id; +char * opt_cert = NULL; +char * opt_outfile = NULL; +char * opt_newpin = NULL; + +int quiet = 0; + +#define OPT_CHANGE_PIN 0x100 +#define OPT_LIST_PINS 0x101 +#define OPT_READER 0x102 +#define OPT_PIN_ID 0x103 +#define OPT_NO_CACHE 0x104 + +const struct option options[] = { + { "learn-card", 0, 0, 'L' }, + { "read-certificate", 1, 0, 'r' }, + { "list-certificates", 0, 0, 'c' }, + { "list-pins", 0, 0, OPT_LIST_PINS }, + { "change-pin", 0, 0, OPT_CHANGE_PIN }, + { "list-keys", 0, 0, 'k' }, + { "reader", 1, 0, OPT_READER }, + { "output", 1, 0, 'o' }, + { "quiet", 0, 0, 'q' }, + { "debug", 0, 0, 'd' }, + { "no-cache", 0, 0, OPT_NO_CACHE }, + { "pin-id", 1, 0, 'p' }, + { 0, 0, 0, 0 } +}; + +const char *option_help[] = { + "Stores card info to cache", + "Reads certificate with ID ", + "Lists certificates", + "Lists PIN codes", + "Changes the PIN code", + "Lists private keys", + "Uses reader number ", + "Outputs to file ", + "Quiet operation", + "Debug output -- may be supplied several times", + "Disable card caching", + "The auth ID of the PIN to use", +}; + +struct sc_context *ctx = NULL; +struct sc_card *card = NULL; +struct sc_pkcs15_card *p15card = NULL; + +int list_certificates(void) +{ + int r, i; + + r = sc_pkcs15_enum_certificates(p15card); + if (r < 0) { + fprintf(stderr, "Certificate enumeration failed: %s\n", sc_strerror(r)); + return 1; + } + if (!quiet) + printf("Card has %d certificate(s).\n\n", p15card->cert_count); + for (i = 0; i < p15card->cert_count; i++) { + struct sc_pkcs15_cert_info *cinfo = &p15card->cert_info[i]; + sc_pkcs15_print_cert_info(cinfo); + printf("\n"); + } + return 0; +} + +int print_pem_certificate(struct sc_pkcs15_cert *cert) +{ + int r; + u8 buf[2048]; + FILE *outf; + + r = sc_base64_encode(cert->data, cert->data_len, buf, + sizeof(buf), 64); + if (r) { + fprintf(stderr, "Base64 encoding failed: %s\n", sc_strerror(r)); + return 1; + } + if (opt_outfile != NULL) { + outf = fopen(opt_outfile, "w"); + if (outf == NULL) { + fprintf(stderr, "Error opening file '%s': %s\n", + opt_outfile, strerror(errno)); + return 2; + } + } else + outf = stdout; + fprintf(outf, "-----BEGIN CERTIFICATE-----\n%s-----END CERTIFICATE-----\n", + buf); + if (outf != stdout) + fclose(outf); + return 0; +} + +int read_certificate(void) +{ + int r, i; + struct sc_pkcs15_id id; + + id.len = SC_PKCS15_MAX_ID_SIZE; + sc_pkcs15_hex_string_to_id(opt_cert, &id); + + r = sc_pkcs15_enum_certificates(p15card); + if (r < 0) { + fprintf(stderr, "Certificate enumeration failed: %s\n", sc_strerror(r)); + return 1; + } + + for (i = 0; i < p15card->cert_count; i++) { + struct sc_pkcs15_cert_info *cinfo = &p15card->cert_info[i]; + struct sc_pkcs15_cert *cert; + + if (sc_pkcs15_compare_id(&id, &cinfo->id) != 1) + continue; + + if (!quiet) + printf("Reading certificate with ID '%s'\n", opt_cert); + r = sc_pkcs15_read_certificate(p15card, cinfo, &cert); + if (r) { + fprintf(stderr, "Certificate read failed: %s\n", sc_strerror(r)); + return 1; + } + r = print_pem_certificate(cert); + sc_pkcs15_free_certificate(cert); + return r; + } + fprintf(stderr, "Certificate with ID '%s' not found.\n", opt_cert); + return 2; +} + +int list_private_keys(void) +{ + int r, i; + + r = sc_pkcs15_enum_private_keys(p15card); + if (r < 0) { + fprintf(stderr, "Private key enumeration failed: %s\n", sc_strerror(r)); + return 1; + } + if (!quiet) + printf("Card has %d private key(s).\n\n", p15card->prkey_count); + for (i = 0; i < p15card->prkey_count; i++) { + struct sc_pkcs15_prkey_info *pinfo = &p15card->prkey_info[i]; + sc_pkcs15_print_prkey_info(pinfo); + printf("\n"); + } + return 0; +} + +u8 * get_pin(const char *prompt, struct sc_pkcs15_pin_info **pin_out) +{ + int r; + char buf[80]; + char *pincode; + struct sc_pkcs15_pin_info *pinfo; + + if (pin_out != NULL) + pinfo = *pin_out; + + if (pinfo == NULL && opt_pin_id == NULL) { + r = sc_pkcs15_enum_pins(p15card); + if (r < 0) { + fprintf(stderr, "PIN code enumeration failed: %s\n", sc_strerror(r)); + return NULL; + } + if (r == 0) { + fprintf(stderr, "No PIN codes found.\n"); + return NULL; + } + pinfo = &p15card->pin_info[0]; + } else if (pinfo == NULL) { + struct sc_pkcs15_id pin_id; + + sc_pkcs15_hex_string_to_id(opt_pin_id, &pin_id); + r = sc_pkcs15_find_pin_by_auth_id(p15card, &pin_id, &pinfo); + if (r) { + fprintf(stderr, "Unable to find PIN code: %s\n", sc_strerror(r)); + return NULL; + } + } + + if (pin_out != NULL) + *pin_out = pinfo; + + sprintf(buf, "%s [%s]: ", prompt, pinfo->com_attr.label); + while (1) { + pincode = getpass(buf); + if (strlen(pincode) == 0) + return NULL; + if (strlen(pincode) < pinfo->min_length) { + printf("PIN code too short, try again.\n"); + continue; + } + if (strlen(pincode) > pinfo->stored_length) { + printf("PIN code too long, try again.\n"); + continue; + } + return (u8 *) strdup(pincode); + } +} + +int list_pins(void) +{ + int r, i; + + r = sc_pkcs15_enum_pins(p15card); + if (r < 0) { + fprintf(stderr, "PIN code enumeration failed: %s\n", sc_strerror(r)); + return 1; + } + if (!quiet) + printf("Card has %d PIN code(s).\n\n", p15card->pin_count); + for (i = 0; i < p15card->pin_count; i++) { + struct sc_pkcs15_pin_info *pinfo = &p15card->pin_info[i]; + sc_pkcs15_print_pin_info(pinfo); + printf("\n"); + } + return 0; +} + +int change_pin(void) +{ + struct sc_pkcs15_pin_info *pinfo = NULL; + u8 *pincode, *newpin; + int r; + + pincode = get_pin("Enter old PIN", &pinfo); + if (pincode == NULL) + return 2; + if (strlen((char *) pincode) == 0) { + fprintf(stderr, "No PIN code supplied.\n"); + return 2; + } + while (1) { + u8 *newpin2; + + newpin = get_pin("Enter new PIN", &pinfo); + if (newpin == NULL || strlen((char *) newpin) == 0) + return 2; + newpin2 = get_pin("Enter new PIN again", &pinfo); + if (newpin2 == NULL || strlen((char *) newpin2) == 0) + return 2; + if (strcmp((char *) newpin, (char *) newpin2) == 0) { + free(newpin2); + break; + } + printf("PIN codes do not match, try again.\n"); + free(newpin); + free(newpin2); + } + r = sc_pkcs15_change_pin(p15card, pinfo, pincode, strlen((char *) pincode), + newpin, strlen((char *) newpin)); + if (r == SC_ERROR_PIN_CODE_INCORRECT) { + fprintf(stderr, "PIN code incorrect; tries left: %d\n", pinfo->tries_left); + return 3; + } else if (r) { + fprintf(stderr, "PIN code change failed: %s\n", sc_strerror(r)); + return 2; + } + if (!quiet) + printf("PIN code changed successfully.\n"); + return 0; +} + +static int generate_cert_filename(struct sc_pkcs15_card *p15card, + const struct sc_pkcs15_cert_info *info, + char *fname, int len) +{ + char *homedir; + char cert_id[SC_PKCS15_MAX_ID_SIZE*2+1]; + int i, r; + + homedir = getenv("HOME"); + if (homedir == NULL) + return -1; + cert_id[0] = 0; + for (i = 0; i < info->id.len; i++) { + char tmp[3]; + + sprintf(tmp, "%02X", info->id.value[i]); + strcat(cert_id, tmp); + } + r = snprintf(fname, len, "%s/%s/%s_%s_%s.crt", homedir, + SC_PKCS15_CACHE_DIR, p15card->label, + p15card->serial_number, cert_id); + if (r < 0) + return -1; + return 0; +} + +int learn_card(void) +{ + struct stat stbuf; + char fname[512], *home; + int r, i; + + home = getenv("HOME"); + if (home == NULL) { + fprintf(stderr, "No $HOME environment variable set.\n"); + return 1; + } + sprintf(fname, "%s/%s", home, SC_PKCS15_CACHE_DIR); + r = stat(fname, &stbuf); + if (r) { + printf("No '%s' directory found, creating...\n", fname); + r = mkdir(fname, 0700); + if (r) { + perror("Directory creation failed"); + return 1; + } + } + printf("Using cache directory '%s'.\n", fname); + r = sc_pkcs15_enum_certificates(p15card); + if (r < 0) { + fprintf(stderr, "Certificate enumeration failed: %s\n", sc_strerror(r)); + return 1; + } + printf("Caching %d certificate(s)...\n", r); + p15card->use_cache = 0; + for (i = 0; i < p15card->cert_count; i++) { + struct sc_pkcs15_cert_info *cinfo = &p15card->cert_info[i]; + struct sc_pkcs15_cert *cert; + FILE *crtf; + + printf("Reading certificate: %s...\n", cinfo->com_attr.label); + r = sc_pkcs15_read_certificate(p15card, cinfo, &cert); + if (r) { + fprintf(stderr, "Certificate read failed: %s\n", sc_strerror(r)); + return 1; + } + r = generate_cert_filename(p15card, cinfo, fname, sizeof(fname)); + if (r) + return 1; + crtf = fopen(fname, "w"); + if (crtf == NULL) { + perror(fname); + return 1; + } + fwrite(cert->data, cert->data_len, 1, crtf); + fclose(crtf); + + sc_pkcs15_free_certificate(cert); + } + + return 0; +} + +int main(int argc, char * const argv[]) +{ + int err = 0, r, c, long_optind = 0; + int do_read_cert = 0; + int do_list_certs = 0; + int do_list_pins = 0; + int do_list_prkeys = 0; + int do_change_pin = 0; + int do_learn_card = 0; + int action_count = 0; + + while (1) { + c = getopt_long(argc, argv, "r:cko:qdp:L", options, &long_optind); + if (c == -1) + break; + if (c == '?') + print_usage_and_die("pkcs15-tool"); + switch (c) { + case 'r': + opt_cert = optarg; + do_read_cert = 1; + action_count++; + break; + case 'c': + do_list_certs = 1; + action_count++; + break; + case OPT_CHANGE_PIN: + do_change_pin = 1; + action_count++; + break; + case OPT_LIST_PINS: + do_list_pins = 1; + action_count++; + break; + case 'k': + do_list_prkeys = 1; + action_count++; + break; + case 'L': + do_learn_card = 1; + action_count++; + break; + case OPT_READER: + opt_reader = atoi(optarg); + break; + case 'o': + opt_outfile = optarg; + break; + case 'q': + quiet++; + break; + case 'd': + opt_debug++; + break; + case 'p': + opt_pin_id = optarg; + break; + case OPT_NO_CACHE: + opt_no_cache++; + break; + } + } + if (action_count == 0) + print_usage_and_die("pkcs15-tool"); + r = sc_establish_context(&ctx); + if (r) { + fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); + return 1; + } + ctx->use_std_output = 1; + ctx->debug = opt_debug; + if (opt_no_cache) + ctx->use_cache = 0; + if (opt_reader >= ctx->reader_count || opt_reader < 0) { + fprintf(stderr, "Illegal reader number. Only %d reader(s) configured.\n", ctx->reader_count); + err = 1; + goto end; + } + if (sc_detect_card(ctx, opt_reader) != 1) { + fprintf(stderr, "Card not present.\n"); + return 3; + } + if (!quiet) + fprintf(stderr, "Connecting to card in reader %s...\n", ctx->readers[opt_reader]); + r = sc_connect_card(ctx, opt_reader, &card); + if (r) { + fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(r)); + err = 1; + goto end; + } + printf("Using card driver: %s\n", card->driver->name); + r = sc_lock(card); + if (r) { + fprintf(stderr, "Unable to lock card: %s\n", sc_strerror(r)); + err = 1; + goto end; + } + if (!quiet) + fprintf(stderr, "Trying to find a PKCS#15 compatible card...\n"); + r = sc_pkcs15_bind(card, &p15card); + if (r) { + fprintf(stderr, "PKCS#15 initialization failed: %s\n", sc_strerror(r)); + err = 1; + goto end; + } + if (!quiet) + fprintf(stderr, "Found %s!\n", p15card->label); + if (do_learn_card) { + if ((err = learn_card())) + goto end; + action_count--; + } + if (do_list_certs) { + if ((err = list_certificates())) + goto end; + action_count--; + } + if (do_read_cert) { + if ((err = read_certificate())) + goto end; + action_count--; + } + if (do_list_prkeys) { + if ((err = list_private_keys())) + goto end; + action_count--; + } + if (do_list_pins) { + if ((err = list_pins())) + goto end; + action_count--; + } + if (do_change_pin) { + if ((err = change_pin())) + goto end; + action_count--; + } +end: + if (p15card) + sc_pkcs15_unbind(p15card); + if (card) { + sc_unlock(card); + sc_disconnect_card(card); + } + if (ctx) + sc_destroy_context(ctx); + return err; +} diff --git a/src/tools/util.c b/src/tools/util.c index 41da1a76..cca512d9 100644 --- a/src/tools/util.c +++ b/src/tools/util.c @@ -55,3 +55,35 @@ void hex_dump_asc(FILE *f, const u8 *in, size_t count) lines++; } } + +void print_usage_and_die(const char *pgmname) +{ + int i = 0; + printf("Usage: %s [OPTIONS]\nOptions:\n", pgmname); + + while (options[i].name) { + char buf[40], tmp[5]; + const char *arg_str; + + if (options[i].val > 0 && options[i].val < 128) + sprintf(tmp, ", -%c", options[i].val); + else + tmp[0] = 0; + switch (options[i].has_arg) { + case 1: + arg_str = " "; + break; + case 2: + arg_str = " [arg]"; + break; + default: + arg_str = ""; + break; + } + sprintf(buf, "--%s%s%s", options[i].name, tmp, arg_str); + printf(" %-30s%s\n", buf, option_help[i]); + i++; + } + exit(2); +} + diff --git a/src/tools/util.h b/src/tools/util.h index 04ee7ffc..e91c1620 100644 --- a/src/tools/util.h +++ b/src/tools/util.h @@ -3,11 +3,27 @@ #ifndef UTIL_H #define UTIL_H +#ifdef HAVE_CONFIG_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif #include +#include +#include +#include +#include +#include +#include #include +extern const struct option options[]; +extern const char *option_help[]; + void print_binary(FILE *f, const u8 *buf, int count); void hex_dump(FILE *f, const u8 *in, int len); void hex_dump_asc(FILE *f, const u8 *in, size_t count); +void print_usage_and_die(const char *pgmname); #endif