- added preliminary CryptoFlex 16k support

- added short names to card drivers
- moved various ISO 7816-9 functions to their correct places
- added write binary support
- renamed opensc-crypt to pkcs15-crypt
- split a part opensc-tool to pkcs15-tool


git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@150 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
jey 2002-01-08 13:56:50 +00:00
parent 1a3e530f01
commit 7ff4c4544c
33 changed files with 1211 additions and 760 deletions

View File

@ -13,5 +13,5 @@ libopensc_la_SOURCES = asn1.c base64.c defaults.c \
libopensc_la_LDFLAGS = -version-info 0:4:0 libopensc_la_LDFLAGS = -version-info 0:4:0
libopensc_la_LIBADD = @LIBPCSC@ 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 noinst_HEADERS = sc-asn1.h sc-log.h sc-internal.h

View File

@ -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ä <juha.yrjola@iki.fi> * Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
* *

View File

@ -1,5 +1,5 @@
/* /*
* sc-base64.c: Base64 converting functions * base64.c: Base64 converting functions
* *
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi> * Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
* *

View File

@ -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ä <juha.yrjola@iki.fi> * Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
* *
@ -25,6 +25,7 @@ static struct sc_card_operations default_ops;
static const struct sc_card_driver default_drv = { static const struct sc_card_driver default_drv = {
NULL, NULL,
"Default driver for unknown cards", "Default driver for unknown cards",
"default",
&default_ops &default_ops
}; };
@ -43,6 +44,7 @@ static int autodetect_class(struct sc_card *card)
int classes[] = { 0x00, 0xC0, 0xB0, 0xA0 }; int classes[] = { 0x00, 0xC0, 0xB0, 0xA0 };
int class_count = sizeof(classes)/sizeof(int); int class_count = sizeof(classes)/sizeof(int);
u8 buf[2]; u8 buf[2];
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
struct sc_apdu apdu; struct sc_apdu apdu;
int i, r; int i, r;
@ -57,7 +59,8 @@ static int autodetect_class(struct sc_card *card)
apdu.data = buf; apdu.data = buf;
apdu.datalen = 2; apdu.datalen = 2;
apdu.lc = 2; apdu.lc = 2;
apdu.resplen = 0; apdu.resplen = sizeof(rbuf);
apdu.resp = rbuf;
apdu.ins = 0xA4; apdu.ins = 0xA4;
apdu.p1 = apdu.p2 = 0; apdu.p1 = apdu.p2 = 0;
r = sc_transmit_apdu(card, &apdu); r = sc_transmit_apdu(card, &apdu);
@ -77,6 +80,25 @@ static int autodetect_class(struct sc_card *card)
card->cla = classes[i]; card->cla = classes[i];
if (card->ctx->debug >= 2) if (card->ctx->debug >= 2)
debug(card->ctx, "detected CLA byte as 0x%02X\n", card->cla); 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; return 0;
} }

View File

@ -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ä <juha.yrjola@iki.fi> * Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
* *
@ -25,6 +25,7 @@ static struct sc_card_operations emv_ops;
static const struct sc_card_driver emv_drv = { static const struct sc_card_driver emv_drv = {
NULL, NULL,
"EMV compatible cards", "EMV compatible cards",
"emv",
&emv_ops &emv_ops
}; };

View File

@ -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ä <juha.yrjola@iki.fi> * Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
* *
@ -22,6 +22,7 @@
#include "sc-log.h" #include "sc-log.h"
static const char *mflex_atrs[] = { 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", "3B:19:14:55:90:01:02:02:00:05:04:B0",
NULL NULL
}; };
@ -30,6 +31,7 @@ static struct sc_card_operations mflex_ops;
static const struct sc_card_driver mflex_drv = { static const struct sc_card_driver mflex_drv = {
NULL, NULL,
"Multiflex/Schlumberger", "Multiflex/Schlumberger",
"mflex",
&mflex_ops &mflex_ops
}; };
@ -70,13 +72,27 @@ static int mflex_init(struct sc_card *card)
return 0; 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, static int parse_flex_sf_reply(struct sc_context *ctx, const u8 *buf, int buflen,
struct sc_file *file) struct sc_file *file)
{ {
const u8 *p = buf + 2; const u8 *p = buf + 2;
u8 b1, b2; u8 b1, b2;
int left; int left;
if (buflen < 14)
return -1;
b1 = *p++; b1 = *p++;
b2 = *p++; b2 = *p++;
file->size = (b1 << 8) + b2; 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); error(ctx, "invalid file type: 0x%02X\n", *p);
return SC_ERROR_UNKNOWN_REPLY; 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 */ p += 3; /* skip ACs */
if (*p++) if (*p++)
file->status = SC_FILE_STATUS_ACTIVATED; file->status = SC_FILE_STATUS_ACTIVATED;
else else
file->status = SC_FILE_STATUS_INVALIDATED; file->status = SC_FILE_STATUS_INVALIDATED;
left = *p++; left = *p++;
/* FIXME: CODEME */
file->magic = SC_FILE_MAGIC;
return 0; return 0;
} }

View File

@ -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ä <juha.yrjola@iki.fi> * Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
* *
@ -29,7 +29,8 @@ static const char *setec_atrs[] = {
static struct sc_card_operations setec_ops; static struct sc_card_operations setec_ops;
static const struct sc_card_driver setec_drv = { static const struct sc_card_driver setec_drv = {
NULL, NULL,
"Setec", "Setec smartcards",
"setec",
&setec_ops &setec_ops
}; };

View File

@ -1,5 +1,5 @@
/* /*
* sc-card.c: General SmartCard functions * card.c: General SmartCard functions
* *
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi> * Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
* *
@ -91,24 +91,36 @@ static int sc_check_apdu(struct sc_context *ctx, const struct sc_apdu *apdu)
{ {
switch (apdu->cse) { switch (apdu->cse) {
case SC_APDU_CASE_1: 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); SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
}
break; break;
case SC_APDU_CASE_2_SHORT: 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); 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); SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
}
break; break;
case SC_APDU_CASE_3_SHORT: 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); SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
}
break; break;
case SC_APDU_CASE_4_SHORT: 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); 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); SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
}
break; break;
case SC_APDU_CASE_2_EXT: case SC_APDU_CASE_2_EXT:
case SC_APDU_CASE_3_EXT: case SC_APDU_CASE_3_EXT:
@ -317,7 +329,7 @@ int sc_connect_card(struct sc_context *ctx,
SCARDHANDLE card_handle; SCARDHANDLE card_handle;
SCARD_READERSTATE_A rgReaderStates[SC_MAX_READERS]; SCARD_READERSTATE_A rgReaderStates[SC_MAX_READERS];
LONG rv; LONG rv;
int i; int i, r = 0;
assert(card_out != NULL); assert(card_out != NULL);
SC_FUNC_CALLED(ctx, 1); SC_FUNC_CALLED(ctx, 1);
@ -339,13 +351,18 @@ int sc_connect_card(struct sc_context *ctx,
if (card == NULL) if (card == NULL)
SC_FUNC_RETURN(ctx, 1, SC_ERROR_OUT_OF_MEMORY); SC_FUNC_RETURN(ctx, 1, SC_ERROR_OUT_OF_MEMORY);
memset(card, 0, sizeof(struct sc_card)); 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], rv = SCardConnect(ctx->pcsc_ctx, ctx->readers[reader],
SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0,
&card_handle, &active_proto); &card_handle, &active_proto);
if (rv != 0) { if (rv != 0) {
error(ctx, "SCardConnect failed: %s\n", pcsc_stringify_error(rv)); error(ctx, "SCardConnect failed: %s\n", pcsc_stringify_error(rv));
free(card); r = -1; /* FIXME: invent a real error value */
return -1; /* FIXME */ goto err;
} }
card->reader = reader; card->reader = reader;
card->ctx = ctx; card->ctx = ctx;
@ -356,8 +373,19 @@ int sc_connect_card(struct sc_context *ctx,
i = SC_MAX_ATR_SIZE; i = SC_MAX_ATR_SIZE;
memcpy(card->atr, rgReaderStates[0].rgbAtr, i); memcpy(card->atr, rgReaderStates[0].rgbAtr, i);
card->atr_len = 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_driver *drv = ctx->card_drivers[i];
const struct sc_card_operations *ops = drv->ops; const struct sc_card_operations *ops = drv->ops;
int r; int r;
@ -370,32 +398,34 @@ int sc_connect_card(struct sc_context *ctx,
continue; continue;
if (ctx->debug >= 3) if (ctx->debug >= 3)
debug(ctx, "matched: %s\n", drv->name); debug(ctx, "matched: %s\n", drv->name);
r = ops->init(card); memcpy(card->ops, ops, sizeof(struct sc_card_operations));
card->ops = ops;
card->driver = drv; card->driver = drv;
r = ops->init(card);
if (r) { if (r) {
error(ctx, "driver '%s' init() failed: %s\n", drv->name, error(ctx, "driver '%s' init() failed: %s\n", drv->name,
sc_strerror(r)); sc_strerror(r));
if (r == SC_ERROR_INVALID_CARD) { if (r == SC_ERROR_INVALID_CARD) {
card->ops = NULL;
card->driver = NULL; card->driver = NULL;
continue; continue;
} }
free(card); goto err;
return r;
} }
break; break;
} }
if (card->ops == NULL) { if (card->driver == NULL) {
error(ctx, "unable to find driver for inserted card\n"); error(ctx, "unable to find driver for inserted card\n");
free(card); r = SC_ERROR_INVALID_CARD;
return SC_ERROR_INVALID_CARD; goto err;
} }
pthread_mutex_init(&card->mutex, NULL); pthread_mutex_init(&card->mutex, NULL);
card->magic = SC_CARD_MAGIC; card->magic = SC_CARD_MAGIC;
*card_out = card; *card_out = card;
SC_FUNC_RETURN(ctx, 1, 0); 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) 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); SCardDisconnect(card->pcsc_card, SCARD_LEAVE_CARD);
pthread_mutex_destroy(&card->mutex); pthread_mutex_destroy(&card->mutex);
free(card->ops);
free(card); free(card);
SC_FUNC_RETURN(ctx, 1, 0); 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; 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 sc_create_file(struct sc_card *card, const struct sc_file *file)
{ {
int r, len; int r;
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
struct sc_apdu apdu;
assert(card != NULL);
SC_FUNC_CALLED(card->ctx, 1); SC_FUNC_CALLED(card->ctx, 1);
len = SC_MAX_APDU_BUFFER_SIZE; if (card->ops->create_file == NULL)
r = construct_fci(file, sbuf, &len); SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NOT_SUPPORTED);
SC_TEST_RET(card->ctx, r, "construct_fci() failed"); r = card->ops->create_file(card, file);
SC_FUNC_RETURN(card->ctx, 1, r);
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));
} }
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; 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); SC_FUNC_CALLED(card->ctx, 1);
sbuf[0] = (file_id >> 8) & 0xFF; if (card->ops->delete_file == NULL)
sbuf[1] = file_id & 0xFF; SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NOT_SUPPORTED);
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); r = card->ops->delete_file(card, path);
apdu.lc = 2; SC_FUNC_RETURN(card->ctx, 1, r);
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));
} }
int sc_read_binary(struct sc_card *card, unsigned int idx, int sc_read_binary(struct sc_card *card, unsigned int idx,
unsigned char *buf, size_t count, unsigned long flags) unsigned char *buf, size_t count, unsigned long flags)
{ {
#define RB_BUF_SIZE 250
int r; int r;
assert(card != NULL && card->ops != NULL && buf != NULL); assert(card != NULL && card->ops != NULL && buf != NULL);
if (card->ctx->debug >= 2) if (card->ctx->debug >= 2)
debug(card->ctx, "sc_read_binary: %d bytes at index %d\n", count, idx); 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; int bytes_read = 0;
unsigned char *p = buf; unsigned char *p = buf;
r = sc_lock(card); r = sc_lock(card);
SC_TEST_RET(card->ctx, r, "sc_lock() failed"); SC_TEST_RET(card->ctx, r, "sc_lock() failed");
while (count > 0) { 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); r = sc_read_binary(card, idx, p, n, flags);
if (r < 0) { if (r < 0) {
sc_unlock(card); sc_unlock(card);
@ -608,11 +582,47 @@ int sc_read_binary(struct sc_card *card, unsigned int idx,
sc_unlock(card); sc_unlock(card);
SC_FUNC_RETURN(card->ctx, 2, bytes_read); 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); r = card->ops->read_binary(card, idx, buf, count, flags);
SC_FUNC_RETURN(card->ctx, 2, r); 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, int sc_select_file(struct sc_card *card,

View File

@ -1,5 +1,5 @@
/* /*
* sc-default.c: Card specific defaults * default.c: Card specific defaults
* *
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi> * Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
* *

23
src/libopensc/emv.c Normal file
View File

@ -0,0 +1,23 @@
/*
* emv.c: EMV functions
*
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
*
* 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 */

42
src/libopensc/emv.h Normal file
View File

@ -0,0 +1,42 @@
/*
* opensc-emv.h: OpenSC EMV header file
*
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
*
* 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

View File

@ -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ä <juha.yrjola@iki.fi> * Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
* *
@ -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)); SC_FUNC_RETURN(card->ctx, 2, sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2));
memcpy(buf, recvbuf, apdu.resplen); 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, 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)); SC_FUNC_RETURN(card->ctx, 2, sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2));
memcpy(buf, recvbuf, apdu.resplen); 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) 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, 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; size_t taglen, len = buflen;
const u8 *tag = NULL, *p = buf; 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; 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 = { static struct sc_card_operations iso_ops = {
NULL, NULL,
}; };
static const struct sc_card_driver iso_driver = { static const struct sc_card_driver iso_driver = {
NULL, NULL,
"ISO 7816-x reference driver", "ISO 7816 reference driver",
"iso7816",
&iso_ops &iso_ops
}; };
@ -324,11 +441,14 @@ const struct sc_card_driver * sc_get_iso7816_driver(void)
{ {
if (iso_ops.match_card == NULL) { if (iso_ops.match_card == NULL) {
memset(&iso_ops, 0, sizeof(iso_ops)); memset(&iso_ops, 0, sizeof(iso_ops));
iso_ops.match_card = no_match; iso_ops.match_card = no_match;
iso_ops.read_binary = iso7816_read_binary; iso_ops.read_binary = iso7816_read_binary;
iso_ops.read_record = iso7816_read_record; iso_ops.read_record = iso7816_read_record;
iso_ops.select_file = iso7816_select_file; iso_ops.write_binary = iso7816_write_binary;
iso_ops.select_file = iso7816_select_file;
iso_ops.get_challenge = iso7816_get_challenge; iso_ops.get_challenge = iso7816_get_challenge;
iso_ops.create_file = iso7816_create_file;
iso_ops.delete_file = iso7816_delete_file;
} }
return &iso_driver; return &iso_driver;
} }

View File

@ -1,5 +1,5 @@
/* /*
* sc-log.c: Miscellaneous logging functions * log.c: Miscellaneous logging functions
* *
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi> * Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
* *

View File

@ -53,7 +53,7 @@ void debug(struct sc_context *ctx, const char *format, ...);
#define SC_TEST_RET(ctx, r, text) {\ #define SC_TEST_RET(ctx, r, text) {\
int _ret = r;\ int _ret = r;\
if (_ret < 0) {\ if (_ret < 0) {\
error(ctx, text": %s\n", sc_strerror(r));\ error(ctx, text": %s\n", sc_strerror(_ret));\
return _ret;\ return _ret;\
}\ }\
} }

View File

@ -0,0 +1,42 @@
/*
* opensc-emv.h: OpenSC EMV header file
*
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
*
* 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

View File

@ -180,7 +180,6 @@ struct sc_pkcs15_defaults {
* a new PKCS#15 card object */ * a new PKCS#15 card object */
int sc_pkcs15_bind(struct sc_card *card, int sc_pkcs15_bind(struct sc_card *card,
struct sc_pkcs15_card **pkcs15_card); struct sc_pkcs15_card **pkcs15_card);
int sc_pkcs15_unbind(struct sc_pkcs15_card *card); int sc_pkcs15_unbind(struct sc_pkcs15_card *card);
int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card, int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card,

View File

@ -104,8 +104,9 @@ extern "C" {
#define SC_AC_NONE 0x00000000 #define SC_AC_NONE 0x00000000
#define SC_AC_CHV1 0x00000001 /* Card Holder Verif. */ #define SC_AC_CHV1 0x00000001 /* Card Holder Verif. */
#define SC_AC_CHV2 0x00000002 #define SC_AC_CHV2 0x00000002
#define SC_AC_TERM 0x00000004 /* Terminal auth */ #define SC_AC_TERM 0x00000004 /* Terminal auth. */
#define SC_AC_PRO 0x00000008 /* Protected mode */ #define SC_AC_PRO 0x00000008 /* Secure Messaging */
#define SC_AC_AUT 0x00000010 /* Key auth. */
#define SC_AC_NEVER 0xFFFFFFFE #define SC_AC_NEVER 0xFFFFFFFE
#define SC_AC_UNKNOWN 0xFFFFFFFF #define SC_AC_UNKNOWN 0xFFFFFFFF
@ -116,6 +117,7 @@ extern "C" {
#define SC_AC_OP_CREATE 3 #define SC_AC_OP_CREATE 3
#define SC_AC_OP_REHABILITATE 4 #define SC_AC_OP_REHABILITATE 4
#define SC_AC_OP_INVALIDATE 5 #define SC_AC_OP_INVALIDATE 5
#define SC_AC_OP_LIST_FILES 6
/* Operations relating to access control (in case of EF) */ /* Operations relating to access control (in case of EF) */
#define SC_AC_OP_READ 0 #define SC_AC_OP_READ 0
@ -124,15 +126,16 @@ extern "C" {
#define SC_AC_OP_ERASE 3 #define SC_AC_OP_ERASE 3
/* rehab and invalidate are the same as in DF case */ /* 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_EF_ID_MASK 0x0001F
#define SC_READ_RECORD_BY_REC_ID 0x00000 #define SC_READ_RECORD_BY_REC_ID 0x00000
#define SC_READ_RECORD_BY_REC_NR 0x00100 #define SC_READ_RECORD_BY_REC_NR 0x00100
/* various maximum values */ /* various maximum values */
#define SC_MAX_CARD_DRIVERS 16 #define SC_MAX_CARD_DRIVERS 16
#define SC_MAX_CARD_DRIVER_SNAME_SIZE 16
#define SC_MAX_READERS 4 #define SC_MAX_READERS 4
#define SC_MAX_APDU_BUFFER_SIZE 255 #define SC_MAX_APDU_BUFFER_SIZE 255
#define SC_MAX_PATH_SIZE 16 #define SC_MAX_PATH_SIZE 16
@ -140,8 +143,8 @@ extern "C" {
#define SC_MAX_ATR_SIZE 33 #define SC_MAX_ATR_SIZE 33
#define SC_MAX_SEC_ATTR_SIZE 16 #define SC_MAX_SEC_ATTR_SIZE 16
#define SC_MAX_PROP_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; typedef unsigned char u8;
@ -221,7 +224,7 @@ struct sc_card {
pthread_mutex_t mutex; pthread_mutex_t mutex;
int lock_count; int lock_count;
const struct sc_card_driver *driver; const struct sc_card_driver *driver;
const struct sc_card_operations *ops; struct sc_card_operations *ops;
void *ops_data; void *ops_data;
unsigned int magic; unsigned int magic;
@ -300,11 +303,17 @@ struct sc_card_operations {
int (*reset_retry_counter)(struct sc_card *card, int ref_qualifier, int (*reset_retry_counter)(struct sc_card *card, int ref_qualifier,
const u8 *puk, size_t puklen, const u8 *puk, size_t puklen,
const u8 *newref, size_t newlen); 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 { struct sc_card_driver {
char *libpath; /* NULL, if compiled in */ char *libpath; /* NULL, if compiled in */
char *name; const char *name;
const char *short_name;
struct sc_card_operations *ops; struct sc_card_operations *ops;
}; };
@ -317,6 +326,7 @@ struct sc_context {
int use_std_output, use_cache; int use_std_output, use_cache;
const struct sc_card_driver *card_drivers[SC_MAX_CARD_DRIVERS+1]; const struct sc_card_driver *card_drivers[SC_MAX_CARD_DRIVERS+1];
const struct sc_card_driver *default_driver;
pthread_mutex_t mutex; 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_establish_context(struct sc_context **ctx);
int sc_destroy_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 sc_connect_card(struct sc_context *ctx,
int reader, struct sc_card **card); int reader, struct sc_card **card);
int sc_disconnect_card(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); struct sc_file *file);
int sc_read_binary(struct sc_card *card, unsigned int idx, u8 * buf, int sc_read_binary(struct sc_card *card, unsigned int idx, u8 * buf,
size_t count, unsigned long flags); 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, int sc_read_record(struct sc_card *card, unsigned int rec_nr, u8 * buf,
size_t count, unsigned long flags); size_t count, unsigned long flags);
int sc_get_challenge(struct sc_card *card, u8 * rndout, size_t len); 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 */ /* ISO 7816-9 */
int sc_create_file(struct sc_card *card, const struct sc_file *file); 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); inline int sc_file_valid(const struct sc_file *file);
void sc_format_path(const char *path_in, struct sc_path *path_out); void sc_format_path(const char *path_in, struct sc_path *path_out);

View File

@ -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ä <juha.yrjola@iki.fi> * Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
* *

View File

@ -1,5 +1,5 @@
/* /*
* sc-default.c: Card specific defaults * pkcs15-default.c: Obsolete
* *
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi> * Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
* *
@ -23,6 +23,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#if 0
static void format_path(struct sc_path *path, const char *str) static void format_path(struct sc_path *path, const char *str)
{ {
int len = 0; 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 }, "07:2A:81:76:84:05:03:01", fineid_pkcs15_defaults, 2 },
{ NULL, NULL, 0 } { NULL, NULL, 0 }
}; };
#endif

View File

@ -153,6 +153,8 @@ static int parse_dir(const u8 * buf, size_t buflen, struct sc_pkcs15_card *card)
card->label = strdup((char *) label); card->label = strdup((char *) label);
else else
card->label = strdup("(unknown)"); card->label = strdup("(unknown)");
if (path_len > SC_MAX_PATH_SIZE)
return -1;
memcpy(card->file_app.path.value, path, path_len); memcpy(card->file_app.path.value, path, path_len);
card->file_app.path.len = path_len; card->file_app.path.len = path_len;
card->file_app.path.type = SC_PATH_TYPE_PATH; 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) static const struct sc_pkcs15_defaults * find_defaults(u8 *dir, int dirlen)
{ {
#if 0 /* FIXME: CODEME */
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
return NULL; return NULL;
} }
@ -350,6 +331,19 @@ error:
SC_FUNC_RETURN(ctx, 1, err); 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) int sc_pkcs15_unbind(struct sc_pkcs15_card *p15card)
{ {
assert(p15card != NULL); 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) 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); return sc_hex_to_bin(in, out->value, &out->len);
} }

View File

@ -180,7 +180,6 @@ struct sc_pkcs15_defaults {
* a new PKCS#15 card object */ * a new PKCS#15 card object */
int sc_pkcs15_bind(struct sc_card *card, int sc_pkcs15_bind(struct sc_card *card,
struct sc_pkcs15_card **pkcs15_card); struct sc_pkcs15_card **pkcs15_card);
int sc_pkcs15_unbind(struct sc_pkcs15_card *card); int sc_pkcs15_unbind(struct sc_pkcs15_card *card);
int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card, int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card,

View File

@ -53,7 +53,7 @@ void debug(struct sc_context *ctx, const char *format, ...);
#define SC_TEST_RET(ctx, r, text) {\ #define SC_TEST_RET(ctx, r, text) {\
int _ret = r;\ int _ret = r;\
if (_ret < 0) {\ if (_ret < 0) {\
error(ctx, text": %s\n", sc_strerror(r));\ error(ctx, text": %s\n", sc_strerror(_ret));\
return _ret;\ return _ret;\
}\ }\
} }

View File

@ -146,22 +146,21 @@ int sc_establish_context(struct sc_context **ctx_out)
ctx = malloc(sizeof(struct sc_context)); ctx = malloc(sizeof(struct sc_context));
if (ctx == NULL) if (ctx == NULL)
return SC_ERROR_OUT_OF_MEMORY; return SC_ERROR_OUT_OF_MEMORY;
ctx->use_std_output = 0; memset(ctx, sizeof(struct sc_context), 0);
ctx->use_cache = 1; ctx->use_cache = 1;
ctx->debug = 0;
rv = SCardEstablishContext(SCARD_SCOPE_GLOBAL, "localhost", NULL, rv = SCardEstablishContext(SCARD_SCOPE_GLOBAL, "localhost", NULL,
&ctx->pcsc_ctx); &ctx->pcsc_ctx);
if (rv != SCARD_S_SUCCESS) if (rv != SCARD_S_SUCCESS)
return SC_ERROR_CONNECTING_TO_RES_MGR; return SC_ERROR_CONNECTING_TO_RES_MGR;
SCardListReaders(ctx->pcsc_ctx, NULL, NULL, SCardListReaders(ctx->pcsc_ctx, NULL, NULL,
(LPDWORD) & reader_buf_size); (LPDWORD) &reader_buf_size);
if (reader_buf_size < 2) { if (reader_buf_size < 2) {
free(ctx); free(ctx);
return SC_ERROR_NO_READERS_FOUND; return SC_ERROR_NO_READERS_FOUND;
} }
reader_buf = (char *) malloc(sizeof(char) * reader_buf_size); reader_buf = (char *) malloc(sizeof(char) * reader_buf_size);
SCardListReaders(ctx->pcsc_ctx, mszGroups, reader_buf, SCardListReaders(ctx->pcsc_ctx, mszGroups, reader_buf,
(LPDWORD) & reader_buf_size); (LPDWORD) &reader_buf_size);
p = reader_buf; p = reader_buf;
ctx->reader_count = 0; ctx->reader_count = 0;
do { do {
@ -209,6 +208,30 @@ int sc_destroy_context(struct sc_context *ctx)
return 0; 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) void sc_format_path(const char *str, struct sc_path *path)
{ {
int len = 0; int len = 0;

View File

@ -4,7 +4,7 @@ INCLUDES = @CFLAGS_PCSC@ @CFLAGS_OPENSC@
LDFLAGS = @LDFLAGS@ @LIBOPENSC@ LDFLAGS = @LDFLAGS@ @LIBOPENSC@
noinst_PROGRAMS = base64 hst-test lottery p15dump \ noinst_PROGRAMS = base64 hst-test lottery p15dump \
pintest prngtest pintest prngtest filetest
SRC = sc-test.c SRC = sc-test.c
INC = sc-test.h INC = sc-test.h
@ -15,3 +15,4 @@ lottery_SOURCES = lottery.c $(SRC) $(INC)
p15dump_SOURCES = p15dump.c $(SRC) $(INC) p15dump_SOURCES = p15dump.c $(SRC) $(INC)
pintest_SOURCES = pintest.c $(SRC) $(INC) pintest_SOURCES = pintest.c $(SRC) $(INC)
prngtest_SOURCES = prngtest.c $(SRC) $(INC) prngtest_SOURCES = prngtest.c $(SRC) $(INC)
filetest_SOURCES = filetest.c $(SRC) $(INC)

18
src/tests/filetest.c Normal file
View File

@ -0,0 +1,18 @@
#include "sc-test.h"
#include "opensc.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
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;
}

View File

@ -8,47 +8,29 @@
#include <opensc.h> #include <opensc.h>
#include <opensc-pkcs15.h> #include <opensc-pkcs15.h>
#include "sc-test.h" #include "sc-test.h"
#include "../libopensc/sc-log.h"
struct sc_pkcs15_card *p15card; struct sc_pkcs15_card *p15card;
int test() int test()
{ {
struct sc_file file; struct sc_file file;
struct sc_path path;
struct sc_apdu apdu; struct sc_apdu apdu;
struct sc_path path;
u8 rbuf[MAX_BUFFER_SIZE], sbuf[MAX_BUFFER_SIZE]; u8 rbuf[MAX_BUFFER_SIZE], sbuf[MAX_BUFFER_SIZE];
int r; int r;
sc_lock(card); sc_lock(card);
#if 1 sc_format_path("I3F00", &path);
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;
r = sc_select_file(card, &path, &file); r = sc_select_file(card, &path, &file);
ctx->debug = 0;
if (r) { if (r) {
fprintf(stderr, "sc_select_file failed: %s\n", sc_strerror(r)); printf("SELECT FILE (MF) failed: %s\n", sc_strerror(r));
goto err; return -1;
} }
r = sc_pkcs15_verify_pin(p15card, &p15card->pin_info[0], (const u8 *) "\x31\x32\x33\x34", 4); ctx->debug = 5;
if (r) { #if 1
fprintf(stderr, "PIN code verification failed: %s\n", sc_strerror(r));
goto err;
}
#endif
ctx->debug = 3;
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0, 1); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0, 1);
apdu.lc = 8; apdu.lc = 8;
apdu.data = sbuf; apdu.data = sbuf;
@ -61,27 +43,27 @@ int test()
fprintf(stderr, "transmit failed: %s\n", sc_strerror(r)); fprintf(stderr, "transmit failed: %s\n", sc_strerror(r));
goto err; goto err;
} }
r = sc_delete_file(card, 0x5110); sc_format_path("I1234", &path);
r = sc_delete_file(card, &path);
if (r) { if (r) {
fprintf(stderr, "fail: %s\n", sc_strerror(r)); fprintf(stderr, "fail: %s\n", sc_strerror(r));
goto err; goto err;
} }
return 0; return 0;
#endif
memset(&file, 0, sizeof(file)); memset(&file, 0, sizeof(file));
file.id = 0x5110; file.id = 0x1234;
file.sec_attr_len = 6; file.sec_attr_len = 6;
memcpy(file.sec_attr, "\x00\x00\x00\x00\x00\x00", 6); memcpy(file.sec_attr, "\x00\x00\x00\x00\x00\x00", 6);
file.prop_attr_len = 3; 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.size = 32;
file.type = SC_FILE_TYPE_WORKING_EF; file.type = SC_FILE_TYPE_WORKING_EF;
file.ef_structure = SC_FILE_EF_TRANSPARENT; file.ef_structure = SC_FILE_EF_TRANSPARENT;
ctx->debug = 1; ctx->debug = 5;
r = sc_create_file(card, &file); r = sc_create_file(card, &file);
err: err:
sc_unlock(card); sc_unlock(card);
return r; return r;
@ -92,15 +74,35 @@ int test2()
int r; int r;
struct sc_path path; struct sc_path path;
struct sc_file file; 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); r = sc_select_file(card, &path, &file);
if (r) { if (r) {
fprintf(stderr, "SELECT FILE failed: %s\n", sc_strerror(r)); fprintf(stderr, "SELECT FILE failed: %s\n", sc_strerror(r));
return 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; return 0;
} }
@ -156,7 +158,7 @@ int main(int argc, char **argv)
return 1; return 1;
ctx->debug = 3; ctx->debug = 3;
if (test3()) if (test())
return 1; return 1;
sc_test_cleanup(); sc_test_cleanup();

View File

@ -16,12 +16,13 @@ int sc_test_init(int *argc, char *argv[])
{ {
int i, c; int i, c;
printf("Using libsc version %s.\n", sc_version); printf("Using libopensc version %s.\n", sc_version);
i = sc_establish_context(&ctx); i = sc_establish_context(&ctx);
if (i < 0) { if (i < 0) {
printf("sc_establish_context() failed (%d)\n", i); printf("sc_establish_context() failed (%d)\n", i);
return i; return i;
} }
ctx->use_std_output = 1;
i = sc_detect_card(ctx, 0); i = sc_detect_card(ctx, 0);
printf("Card %s.\n", i == 1 ? "present" : "absent"); printf("Card %s.\n", i == 1 ? "present" : "absent");
if (i < 0) { if (i < 0) {

View File

@ -3,10 +3,12 @@
INCLUDES = @CFLAGS_PCSC@ @CFLAGS_OPENSC@ INCLUDES = @CFLAGS_PCSC@ @CFLAGS_OPENSC@
LDFLAGS = @LDFLAGS@ @LIBOPENSC@ 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_SOURCES = opensc-tool.c util.c
opensc_tool_LDADD = @GETOPTSRC@ 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 noinst_HEADERS = util.h

View File

@ -18,25 +18,17 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#ifdef HAVE_CONFIG_H #include "util.h"
#include <config.h>
#endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#include <stdio.h> #include <stdio.h>
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <opensc.h> #include <opensc.h>
#include <opensc-pkcs15.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <ctype.h> #include <ctype.h>
#include "util.h"
#define OPT_CHANGE_PIN 0x100 #define OPT_CHANGE_PIN 0x100
#define OPT_LIST_PINS 0x101 #define OPT_LIST_PINS 0x101
#define OPT_READER 0x102 #define OPT_READER 0x102
@ -44,30 +36,19 @@
#define OPT_NO_CACHE 0x104 #define OPT_NO_CACHE 0x104
int opt_reader = 0, opt_no_cache = 0, opt_debug = 0; int opt_reader = 0, opt_no_cache = 0, opt_debug = 0;
char * opt_pin_id; char * opt_apdus[8];
char * opt_cert = NULL; int opt_apdu_count = 0;
char * opt_outfile = NULL;
char * opt_newpin = NULL;
char * opt_apdu = NULL;
int quiet = 0; int quiet = 0;
const struct option options[] = { const struct option options[] = {
{ "list-readers", 0, 0, 'l' }, { "list-readers", 0, 0, 'l' },
{ "list-drivers", 0, 0, 'D' }, { "list-drivers", 0, 0, 'D' },
{ "list-files", 0, 0, 'f' }, { "list-files", 0, 0, 'f' },
{ "learn-card", 0, 0, 'L' },
{ "send-apdu", 1, 0, 's' }, { "send-apdu", 1, 0, 's' },
{ "read-certificate", 1, 0, 'r' }, { "reader", 1, 0, 'r' },
{ "list-certificates", 0, 0, 'c' }, { "card-driver", 1, 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' },
{ "quiet", 0, 0, 'q' }, { "quiet", 0, 0, 'q' },
{ "debug", 0, 0, 'd' }, { "debug", 0, 0, 'd' },
{ "no-cache", 0, 0, OPT_NO_CACHE },
{ "pin-id", 1, 0, 'p' },
{ 0, 0, 0, 0 } { 0, 0, 0, 0 }
}; };
@ -75,55 +56,15 @@ const char *option_help[] = {
"Lists all configured readers", "Lists all configured readers",
"Lists all installed card drivers", "Lists all installed card drivers",
"Recursively lists files stored on card", "Recursively lists files stored on card",
"Stores card info to cache [P15]",
"Sends an APDU in format AA:BB:CC:DD:EE:FF...", "Sends an APDU in format AA:BB:CC:DD:EE:FF...",
"Reads certificate with ID <arg> [P15]", "Uses reader number <arg> [0]",
"Lists certificates [P15]", "Forces the use of driver <arg> [auto-detect]",
"Lists PIN codes [P15]",
"Changes the PIN code [P15]",
"Lists private keys [P15]",
"Uses reader number <arg>",
"Outputs to file <arg>",
"Quiet operation", "Quiet operation",
"Debug output -- may be supplied several times", "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_context *ctx = NULL;
struct sc_card *card = 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 = " <arg>";
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) int list_readers(void)
{ {
@ -135,7 +76,7 @@ int list_readers(void)
} }
printf("Configured readers:\n"); printf("Configured readers:\n");
for (i = 0; i < ctx->reader_count; i++) { 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; return 0;
} }
@ -150,228 +91,12 @@ int list_drivers(void)
} }
printf("Configured card drivers:\n"); printf("Configured card drivers:\n");
for (i = 0; ctx->card_drivers[i] != NULL; i++) { 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; 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) const char * print_acl(unsigned int acl)
{ {
static char line[80]; static char line[80];
@ -391,6 +116,9 @@ const char * print_acl(unsigned int acl)
strcat(line, "TERM "); strcat(line, "TERM ");
if (acl & SC_AC_PRO) if (acl & SC_AC_PRO)
strcat(line, "PROT "); strcat(line, "PROT ");
if (acl & SC_AC_AUT)
strcat(line, "AUTH ");
line[strlen(line)-1] = 0; /* get rid of trailing space */ line[strlen(line)-1] = 0; /* get rid of trailing space */
return line; return line;
} }
@ -400,7 +128,8 @@ int print_file(struct sc_card *card, const struct sc_file *file, const struct sc
int r; int r;
const char *tmps; const char *tmps;
const char *ac_ops_df[] = { const char *ac_ops_df[] = {
"select", "lock", "delete", "create", "rehab", "inval" "select", "lock", "delete", "create", "rehab", "inval",
"list"
}; };
const char *ac_ops_ef[] = { const char *ac_ops_ef[] = {
"read", "update", "write", "erase", "rehab", "inval" "read", "update", "write", "erase", "rehab", "inval"
@ -528,158 +257,80 @@ int list_files(void)
return r; 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) int send_apdu(void)
{ {
struct sc_apdu apdu; struct sc_apdu apdu;
u8 buf[MAX_BUFFER_SIZE], sbuf[MAX_BUFFER_SIZE], u8 buf[MAX_BUFFER_SIZE], sbuf[MAX_BUFFER_SIZE],
rbuf[MAX_BUFFER_SIZE], *p = buf; rbuf[MAX_BUFFER_SIZE], *p;
size_t len, len0 = sizeof(buf), r; size_t len, len0, r;
int c;
sc_hex_to_bin(opt_apdu, buf, &len0);
if (len0 < 4) { for (c = 0; c < opt_apdu_count; c++) {
fprintf(stderr, "APDU too short (must be at least 4 bytes).\n"); len0 = sizeof(buf);
return 2; sc_hex_to_bin(opt_apdus[c], buf, &len0);
} if (len0 < 4) {
len = len0; fprintf(stderr, "APDU too short (must be at least 4 bytes).\n");
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; return 2;
} }
len -= apdu.lc; len = len0;
if (len) { 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++; apdu.le = *p++;
len--; len--;
apdu.cse = SC_APDU_CASE_4_SHORT; apdu.cse = SC_APDU_CASE_2_SHORT;
} else } else
apdu.cse = SC_APDU_CASE_3_SHORT; apdu.cse = SC_APDU_CASE_1;
if (len) { printf("Sending: ");
fprintf(stderr, "APDU too long (%d bytes extra).\n", len); for (r = 0; r < len0; r++)
return 2; 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) { printf("Received (SW1=0x%02X, SW2=0x%02X)%s\n", apdu.sw1, apdu.sw2,
apdu.le = *p++; apdu.resplen ? ":" : "");
len--; if (apdu.resplen)
apdu.cse = SC_APDU_CASE_2_SHORT; hex_dump_asc(stdout, apdu.resp, apdu.resplen);
} 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);
return 0; return 0;
} }
@ -688,28 +339,18 @@ int main(int argc, char * const argv[])
int err = 0, r, c, long_optind = 0; int err = 0, r, c, long_optind = 0;
int do_list_readers = 0; int do_list_readers = 0;
int do_list_drivers = 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_files = 0;
int do_list_prkeys = 0;
int do_change_pin = 0;
int do_send_apdu = 0; int do_send_apdu = 0;
int do_learn_card = 0;
int action_count = 0; int action_count = 0;
const char *opt_driver = NULL;
while (1) { 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) if (c == -1)
break; break;
if (c == '?') if (c == '?')
print_usage_and_die(); print_usage_and_die("opensc-tool");
switch (c) { switch (c) {
case 'r':
opt_cert = optarg;
do_read_cert = 1;
action_count++;
break;
case 'l': case 'l':
do_list_readers = 1; do_list_readers = 1;
action_count++; action_count++;
@ -722,53 +363,29 @@ int main(int argc, char * const argv[])
do_list_files = 1; do_list_files = 1;
action_count++; action_count++;
break; break;
case 'c':
do_list_certs = 1;
action_count++;
break;
case 's': case 's':
opt_apdu = optarg; opt_apdus[opt_apdu_count] = optarg;
do_send_apdu++; do_send_apdu++;
action_count++; if (opt_apdu_count == 0)
action_count++;
opt_apdu_count++;
break; break;
case OPT_CHANGE_PIN: case 'r':
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); opt_reader = atoi(optarg);
break; break;
case 'o':
opt_outfile = optarg;
break;
case 'q': case 'q':
quiet++; quiet++;
break; break;
case 'd': case 'd':
opt_debug++; opt_debug++;
break; break;
case 'p': case 'c':
opt_pin_id = optarg; opt_driver = optarg;
break;
case OPT_NO_CACHE:
opt_no_cache++;
break; break;
} }
} }
if (action_count == 0) if (action_count == 0)
print_usage_and_die(); print_usage_and_die("opensc-tool");
r = sc_establish_context(&ctx); r = sc_establish_context(&ctx);
if (r) { if (r) {
fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(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) { if (sc_detect_card(ctx, opt_reader) != 1) {
fprintf(stderr, "Card not present.\n"); 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) if (!quiet)
fprintf(stderr, "Connecting to card in reader %s...\n", ctx->readers[opt_reader]); 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; goto end;
action_count--; 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: end:
if (p15card)
sc_pkcs15_unbind(p15card);
if (card) { if (card) {
sc_unlock(card); sc_unlock(card);
sc_disconnect_card(card); sc_disconnect_card(card);

View File

@ -18,23 +18,16 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#ifdef HAVE_CONFIG_H #include "util.h"
#include <config.h>
#endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#include <stdio.h> #include <stdio.h>
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <opensc.h> #include <opensc.h>
#include <opensc-pkcs15.h> #include <opensc-pkcs15.h>
#include "util.h"
int opt_reader = 0, opt_pin = 0, quiet = 0; int opt_reader = 0, opt_pin = 0, quiet = 0;
int opt_debug = 0; int opt_debug = 0;
char * opt_pincode = NULL, * opt_key_id = NULL; char * opt_pincode = NULL, * opt_key_id = NULL;
@ -69,44 +62,13 @@ const char *option_help[] = {
"Quiet operation", "Quiet operation",
"Debug output -- may be supplied several times", "Debug output -- may be supplied several times",
"Uses password (PIN) <arg>", "Uses password (PIN) <arg>",
"The auth ID of the PIN to use [P15]", "The auth ID of the PIN to use",
}; };
struct sc_context *ctx = NULL; struct sc_context *ctx = NULL;
struct sc_card *card = NULL; struct sc_card *card = NULL;
struct sc_pkcs15_card *p15card = 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 = " <arg>";
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 * get_pin(struct sc_pkcs15_pin_info *pinfo)
{ {
char buf[80]; char buf[80];
@ -236,7 +198,7 @@ int main(int argc, char * const argv[])
if (c == -1) if (c == -1)
break; break;
if (c == '?') if (c == '?')
print_usage_and_die(); print_usage_and_die("opensc-crypt");
switch (c) { switch (c) {
case 's': case 's':
do_sign++; do_sign++;
@ -274,7 +236,7 @@ int main(int argc, char * const argv[])
} }
} }
if (action_count == 0) if (action_count == 0)
print_usage_and_die(); print_usage_and_die("opensc-crypt");
r = sc_establish_context(&ctx); r = sc_establish_context(&ctx);
if (r) { if (r) {
fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r));

503
src/tools/pkcs15-tool.c Normal file
View File

@ -0,0 +1,503 @@
#include "util.h"
#include <opensc-pkcs15.h>
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 <arg>",
"Lists certificates",
"Lists PIN codes",
"Changes the PIN code",
"Lists private keys",
"Uses reader number <arg>",
"Outputs to file <arg>",
"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;
}

View File

@ -55,3 +55,35 @@ void hex_dump_asc(FILE *f, const u8 *in, size_t count)
lines++; 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 = " <arg>";
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);
}

View File

@ -3,11 +3,27 @@
#ifndef UTIL_H #ifndef UTIL_H
#define UTIL_H #define UTIL_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <opensc.h> #include <opensc.h>
extern const struct option options[];
extern const char *option_help[];
void print_binary(FILE *f, const u8 *buf, int count); void print_binary(FILE *f, const u8 *buf, int count);
void hex_dump(FILE *f, const u8 *in, int len); void hex_dump(FILE *f, const u8 *in, int len);
void hex_dump_asc(FILE *f, const u8 *in, size_t count); void hex_dump_asc(FILE *f, const u8 *in, size_t count);
void print_usage_and_die(const char *pgmname);
#endif #endif