- 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:
parent
1a3e530f01
commit
7ff4c4544c
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
*
|
||||
|
|
|
@ -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>
|
||||
*
|
||||
|
|
|
@ -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>
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
*
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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>
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
*
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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>
|
||||
*
|
||||
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
*
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
|
@ -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>
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
*
|
||||
|
|
|
@ -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;\
|
||||
}\
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* sc-default.c: Card specific defaults
|
||||
* pkcs15-default.c: Obsolete
|
||||
*
|
||||
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
|
||||
*
|
||||
|
@ -23,6 +23,7 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;\
|
||||
}\
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -8,47 +8,29 @@
|
|||
#include <opensc.h>
|
||||
#include <opensc-pkcs15.h>
|
||||
#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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -18,25 +18,17 @@
|
|||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include "util.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <opensc.h>
|
||||
#include <opensc-pkcs15.h>
|
||||
#include <sys/stat.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#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 <arg> [P15]",
|
||||
"Lists certificates [P15]",
|
||||
"Lists PIN codes [P15]",
|
||||
"Changes the PIN code [P15]",
|
||||
"Lists private keys [P15]",
|
||||
"Uses reader number <arg>",
|
||||
"Outputs to file <arg>",
|
||||
"Uses reader number <arg> [0]",
|
||||
"Forces the use of driver <arg> [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 = " <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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
|
|
@ -18,23 +18,16 @@
|
|||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include "util.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <opensc.h>
|
||||
#include <opensc-pkcs15.h>
|
||||
|
||||
#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) <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_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 = " <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 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));
|
|
@ -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;
|
||||
}
|
|
@ -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 = " <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);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,27 @@
|
|||
#ifndef 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 <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.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 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
|
||||
|
|
Loading…
Reference in New Issue