- added partial support for TCOS 2.0 cards
- default card driver now tries to do a GET RESPONSE instead of SELECT FILE to detect the correct CLA byte - moved security attribute parsing from iso7816.c to card-setec.c - added some more sanity checking to sc_check_apdu - added 'debug' command line option to opensc-explorer git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@210 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
parent
6db645e46c
commit
709727d469
|
@ -11,7 +11,7 @@ libopensc_la_SOURCES = asn1.c base64.c sec.c log.c sc.c card.c iso7816.c \
|
|||
pkcs15.c pkcs15-cert.c pkcs15-pin.c \
|
||||
pkcs15-prkey.c pkcs15-sec.c pkcs15-cache.c \
|
||||
card-setec.c card-flex.c card-gpk.c \
|
||||
card-emv.c card-default.c
|
||||
card-tcos.c card-emv.c card-default.c
|
||||
libopensc_la_LDFLAGS = -version-info 0:5:0
|
||||
|
||||
if HAVE_SSL
|
||||
|
|
|
@ -43,7 +43,6 @@ 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;
|
||||
|
@ -54,26 +53,22 @@ static int autodetect_class(struct sc_card *card)
|
|||
if (card->ctx->debug >= 2)
|
||||
debug(card->ctx, "trying with 0x%02X\n", classes[i]);
|
||||
apdu.cla = classes[i];
|
||||
apdu.cse = SC_APDU_CASE_3_SHORT;
|
||||
memcpy(buf, "\x3F\x00", 2);
|
||||
apdu.data = buf;
|
||||
apdu.datalen = 2;
|
||||
apdu.lc = 2;
|
||||
apdu.resplen = sizeof(rbuf);
|
||||
apdu.resp = rbuf;
|
||||
apdu.ins = 0xA4;
|
||||
apdu.cse = SC_APDU_CASE_1;
|
||||
apdu.ins = 0xC0;
|
||||
apdu.p1 = apdu.p2 = 0;
|
||||
apdu.datalen = 0;
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
if (apdu.sw1 == 0x6E)
|
||||
continue;
|
||||
if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)
|
||||
break;
|
||||
if (apdu.sw1 == 0x61)
|
||||
break;
|
||||
if (apdu.sw1 == 0x6E)
|
||||
continue;
|
||||
if (card->ctx->debug >= 2)
|
||||
debug(card->ctx, "got strange SWs: 0x%02X 0x%02X\n",
|
||||
apdu.sw1, apdu.sw2);
|
||||
break;
|
||||
}
|
||||
if (i == class_count)
|
||||
return -1;
|
||||
|
|
|
@ -111,6 +111,47 @@ static int setec_set_security_env(struct sc_card *card,
|
|||
return iso_ops->set_security_env(card, env, se_num);
|
||||
}
|
||||
|
||||
static unsigned int byte_to_acl(u8 byte)
|
||||
{
|
||||
switch (byte >> 4) {
|
||||
case 0:
|
||||
return SC_AC_NONE;
|
||||
case 1:
|
||||
return SC_AC_CHV1;
|
||||
case 2:
|
||||
return SC_AC_CHV2;
|
||||
case 4:
|
||||
return SC_AC_TERM;
|
||||
case 15:
|
||||
return SC_AC_NEVER;
|
||||
}
|
||||
return SC_AC_UNKNOWN;
|
||||
}
|
||||
|
||||
static void parse_sec_attr(struct sc_file *file, const u8 *buf, size_t len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (len < 6)
|
||||
return;
|
||||
for (i = 0; i < 6; i++)
|
||||
file->acl[i] = byte_to_acl(buf[i]);
|
||||
}
|
||||
|
||||
static int setec_select_file(struct sc_card *card,
|
||||
const struct sc_path *in_path,
|
||||
struct sc_file *file)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = iso_ops->select_file(card, in_path, file);
|
||||
if (r)
|
||||
return r;
|
||||
if (file != NULL)
|
||||
parse_sec_attr(file, file->sec_attr, file->sec_attr_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sc_card_driver * sc_get_driver(void)
|
||||
{
|
||||
const struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
|
||||
|
@ -123,6 +164,7 @@ static const struct sc_card_driver * sc_get_driver(void)
|
|||
iso_ops = iso_drv->ops;
|
||||
setec_ops.create_file = setec_create_file;
|
||||
setec_ops.set_security_env = setec_set_security_env;
|
||||
setec_ops.select_file = setec_select_file;
|
||||
|
||||
return &setec_drv;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* card-setec.c: Support for TCOS 2.0 cards
|
||||
*
|
||||
* 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 "sc-internal.h"
|
||||
#include "sc-log.h"
|
||||
|
||||
static const char *tcos_atrs[] = {
|
||||
"3B:BA:96:00:81:31:86:5D:00:64:05:60:02:03:31:80:90:00:66",
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct sc_card_operations tcos_ops;
|
||||
static const struct sc_card_driver tcos_drv = {
|
||||
NULL,
|
||||
"TCOS 2.0 cards",
|
||||
"tcos",
|
||||
&tcos_ops
|
||||
};
|
||||
|
||||
static int tcos_finish(struct sc_card *card)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcos_match_card(struct sc_card *card)
|
||||
{
|
||||
int i, match = -1;
|
||||
|
||||
for (i = 0; tcos_atrs[i] != NULL; i++) {
|
||||
u8 defatr[SC_MAX_ATR_SIZE];
|
||||
size_t len = sizeof(defatr);
|
||||
const char *atrp = tcos_atrs[i];
|
||||
|
||||
if (sc_hex_to_bin(atrp, defatr, &len))
|
||||
continue;
|
||||
if (len != card->atr_len)
|
||||
continue;
|
||||
if (memcmp(card->atr, defatr, len) != 0)
|
||||
continue;
|
||||
match = i;
|
||||
break;
|
||||
}
|
||||
if (match == -1)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tcos_init(struct sc_card *card)
|
||||
{
|
||||
card->ops_data = NULL;
|
||||
card->cla = 0x00;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sc_card_operations *iso_ops = NULL;
|
||||
|
||||
static int tcos_create_file(struct sc_card *card, struct sc_file *file)
|
||||
{
|
||||
struct sc_file tmp;
|
||||
|
||||
tmp = *file;
|
||||
memcpy(tmp.prop_attr, "\x03\x00\x00", 3);
|
||||
tmp.prop_attr_len = 3;
|
||||
return iso_ops->create_file(card, &tmp);
|
||||
}
|
||||
|
||||
static int tcos_set_security_env(struct sc_card *card,
|
||||
const struct sc_security_env *env,
|
||||
int se_num)
|
||||
{
|
||||
if (env->flags & SC_SEC_ENV_ALG_PRESENT) {
|
||||
struct sc_security_env tmp;
|
||||
|
||||
tmp = *env;
|
||||
tmp.flags &= ~SC_SEC_ENV_ALG_PRESENT;
|
||||
tmp.flags |= SC_SEC_ENV_ALG_REF_PRESENT;
|
||||
if (tmp.algorithm != SC_ALGORITHM_RSA) {
|
||||
error(card->ctx, "Only RSA algorithm supported.\n");
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
tmp.algorithm_ref = 0x00;
|
||||
if (tmp.algorithm_flags & SC_ALGORITHM_RSA_PKCS1_PAD)
|
||||
tmp.algorithm_ref = 0x02;
|
||||
if (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1)
|
||||
tmp.algorithm_ref |= 0x10;
|
||||
return iso_ops->set_security_env(card, &tmp, se_num);
|
||||
|
||||
}
|
||||
return iso_ops->set_security_env(card, env, se_num);
|
||||
}
|
||||
|
||||
static void parse_sec_attr(struct sc_file *file, const u8 *buf, size_t len)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static int tcos_select_file(struct sc_card *card,
|
||||
const struct sc_path *in_path,
|
||||
struct sc_file *file)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = iso_ops->select_file(card, in_path, file);
|
||||
if (r)
|
||||
return r;
|
||||
if (file != NULL)
|
||||
parse_sec_attr(file, file->sec_attr, file->sec_attr_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcos_list_files(struct sc_card *card, u8 *buf, size_t buflen)
|
||||
{
|
||||
struct sc_apdu apdu;
|
||||
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||
u8 p1s[2] = { 0x01, 0x02 };
|
||||
int r, i, count = 0;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xAA, p1s[i], 0);
|
||||
apdu.cla = 0x80;
|
||||
apdu.resp = rbuf;
|
||||
apdu.resplen = sizeof(rbuf);
|
||||
apdu.le = 256;
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
r = sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2);
|
||||
if (r == SC_ERROR_FILE_NOT_FOUND)
|
||||
continue;
|
||||
SC_TEST_RET(card->ctx, r, "Card returned error");
|
||||
if (apdu.resplen > buflen)
|
||||
return SC_ERROR_BUFFER_TOO_SMALL;
|
||||
memcpy(buf, apdu.resp, apdu.resplen);
|
||||
buf += apdu.resplen;
|
||||
buflen -= apdu.resplen;
|
||||
count += apdu.resplen;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct sc_card_driver * sc_get_driver(void)
|
||||
{
|
||||
const struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
|
||||
|
||||
tcos_ops = *iso_drv->ops;
|
||||
tcos_ops.match_card = tcos_match_card;
|
||||
tcos_ops.init = tcos_init;
|
||||
tcos_ops.finish = tcos_finish;
|
||||
if (iso_ops == NULL)
|
||||
iso_ops = iso_drv->ops;
|
||||
tcos_ops.create_file = tcos_create_file;
|
||||
tcos_ops.set_security_env = tcos_set_security_env;
|
||||
tcos_ops.select_file = tcos_select_file;
|
||||
tcos_ops.list_files = tcos_list_files;
|
||||
|
||||
return &tcos_drv;
|
||||
}
|
||||
|
||||
#if 1
|
||||
const struct sc_card_driver * sc_get_tcos_driver(void)
|
||||
{
|
||||
return sc_get_driver();
|
||||
}
|
||||
#endif
|
|
@ -89,6 +89,14 @@ static int _sc_pcscret_to_error(long rv)
|
|||
|
||||
static int sc_check_apdu(struct sc_context *ctx, const struct sc_apdu *apdu)
|
||||
{
|
||||
if (apdu->le > 256) {
|
||||
error(ctx, "Value of Le too big (maximum 256 bytes)\n");
|
||||
SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
|
||||
}
|
||||
if (apdu->lc > 256) {
|
||||
error(ctx, "Value of Lc too big (maximum 256 bytes)\n");
|
||||
SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
|
||||
}
|
||||
switch (apdu->cse) {
|
||||
case SC_APDU_CASE_1:
|
||||
if (apdu->datalen > 0) {
|
||||
|
@ -101,6 +109,10 @@ static int sc_check_apdu(struct sc_context *ctx, const struct sc_apdu *apdu)
|
|||
error(ctx, "Case 2 APDU with data supplied\n");
|
||||
SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
|
||||
}
|
||||
if (apdu->le == 0) {
|
||||
error(ctx, "Case 2 APDU with no response expected\n");
|
||||
SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
|
||||
}
|
||||
if (apdu->resplen < apdu->le) {
|
||||
error(ctx, "Response buffer size < Le\n");
|
||||
SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
|
||||
|
@ -117,6 +129,10 @@ static int sc_check_apdu(struct sc_context *ctx, const struct sc_apdu *apdu)
|
|||
error(ctx, "Case 3 APDU with no data supplied\n");
|
||||
SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
|
||||
}
|
||||
if (apdu->le == 0) {
|
||||
error(ctx, "Case 4 APDU with no response expected\n");
|
||||
SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
|
||||
}
|
||||
if (apdu->resplen < apdu->le) {
|
||||
error(ctx, "Le > response buffer size\n");
|
||||
SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
|
||||
|
@ -197,7 +213,10 @@ static int sc_transceive_t0(struct sc_card *card, struct sc_apdu *apdu)
|
|||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
memcpy(data, apdu->data, data_bytes);
|
||||
data += data_bytes;
|
||||
*data++ = (u8) apdu->le;
|
||||
if (apdu->le == 256)
|
||||
*data++ = 0x00;
|
||||
else
|
||||
*data++ = (u8) apdu->le;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -122,34 +122,6 @@ static int iso7816_update_binary(struct sc_card *card,
|
|||
SC_FUNC_RETURN(card->ctx, 3, count);
|
||||
}
|
||||
|
||||
static unsigned int byte_to_acl(u8 byte)
|
||||
{
|
||||
switch (byte >> 4) {
|
||||
case 0:
|
||||
return SC_AC_NONE;
|
||||
case 1:
|
||||
return SC_AC_CHV1;
|
||||
case 2:
|
||||
return SC_AC_CHV2;
|
||||
case 4:
|
||||
return SC_AC_TERM;
|
||||
case 15:
|
||||
return SC_AC_NEVER;
|
||||
}
|
||||
return SC_AC_UNKNOWN;
|
||||
}
|
||||
|
||||
static void parse_sec_attr(struct sc_file *file, const u8 *buf, size_t len)
|
||||
{
|
||||
/* FIXME: confirm if this is specified in the ISO 7816-9 standard */
|
||||
int i;
|
||||
|
||||
if (len < 6)
|
||||
return;
|
||||
for (i = 0; i < 6; i++)
|
||||
file->acl[i] = byte_to_acl(buf[i]);
|
||||
}
|
||||
|
||||
static void process_fci(struct sc_context *ctx, struct sc_file *file,
|
||||
const u8 *buf, size_t buflen)
|
||||
{
|
||||
|
@ -238,10 +210,10 @@ static void process_fci(struct sc_context *ctx, struct sc_file *file,
|
|||
file->prop_attr_len = taglen;
|
||||
}
|
||||
tag = sc_asn1_find_tag(ctx, p, len, 0x86, &taglen);
|
||||
if (tag != NULL && taglen && taglen <= SC_MAX_SEC_ATTR_SIZE)
|
||||
parse_sec_attr(file, tag, taglen);
|
||||
else
|
||||
file->sec_attr_len = 0;
|
||||
if (tag != NULL && taglen && taglen <= SC_MAX_SEC_ATTR_SIZE) {
|
||||
memcpy(file->sec_attr, tag, taglen);
|
||||
file->sec_attr_len = taglen;
|
||||
}
|
||||
file->magic = SC_FILE_MAGIC;
|
||||
}
|
||||
|
||||
|
@ -260,9 +232,7 @@ static int iso7816_select_file(struct sc_card *card,
|
|||
memcpy(path, in_path->value, in_path->len);
|
||||
pathlen = in_path->len;
|
||||
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0, 0);
|
||||
apdu.resp = buf;
|
||||
apdu.resplen = sizeof(buf);
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0);
|
||||
|
||||
switch (in_path->type) {
|
||||
case SC_PATH_TYPE_FILE_ID:
|
||||
|
@ -287,7 +257,7 @@ static int iso7816_select_file(struct sc_card *card,
|
|||
default:
|
||||
SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_INVALID_ARGUMENTS);
|
||||
}
|
||||
apdu.p2 = 0; /* first record */
|
||||
apdu.p2 = 0; /* first record, return FCI */
|
||||
apdu.lc = pathlen;
|
||||
apdu.data = path;
|
||||
apdu.datalen = pathlen;
|
||||
|
@ -299,9 +269,16 @@ static int iso7816_select_file(struct sc_card *card,
|
|||
for (i = 0; i < SC_MAX_AC_OPS; i++)
|
||||
file->acl[i] = SC_AC_UNKNOWN;
|
||||
file->path = *in_path;
|
||||
}
|
||||
if (file == NULL)
|
||||
|
||||
apdu.resp = buf;
|
||||
apdu.resplen = sizeof(buf);
|
||||
apdu.le = 256;
|
||||
} else {
|
||||
apdu.resplen = 0;
|
||||
apdu.le = 0;
|
||||
apdu.cse = SC_APDU_CASE_3_SHORT;
|
||||
apdu.p2 = 0x0c;
|
||||
}
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
if (file == NULL) {
|
||||
|
@ -489,7 +466,7 @@ static int iso7816_list_files(struct sc_card *card, u8 *buf, size_t buflen)
|
|||
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xAA, 0, 0);
|
||||
apdu.resp = buf;
|
||||
apdu.resplen = buflen;
|
||||
apdu.le = 0;
|
||||
apdu.le = buflen > 256 ? 256 : buflen;
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
if (apdu.resplen == 0)
|
||||
|
|
|
@ -147,12 +147,13 @@ extern "C" {
|
|||
#define SC_READ_RECORD_EF_ID_MASK 0x0001F
|
||||
#define SC_READ_RECORD_BY_REC_ID 0x00000
|
||||
#define SC_READ_RECORD_BY_REC_NR 0x00100
|
||||
#define SC_READ_RECORD_CURRENT 0
|
||||
|
||||
/* 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_APDU_BUFFER_SIZE 258
|
||||
#define SC_MAX_PATH_SIZE 16
|
||||
#define SC_MAX_PIN_SIZE 16
|
||||
#define SC_MAX_ATR_SIZE 33
|
||||
|
@ -505,6 +506,15 @@ int sc_write_binary(struct sc_card *card, unsigned int idx, const u8 * buf,
|
|||
size_t count, unsigned long flags);
|
||||
int sc_update_binary(struct sc_card *card, unsigned int idx, const u8 * buf,
|
||||
size_t count, unsigned long flags);
|
||||
/**
|
||||
* Reads a record from the current (i.e. selected) file.
|
||||
* @param card The card on which to issue the command
|
||||
* @param rec_nr SC_READ_RECORD_CURRENT or a record number beginning from 1
|
||||
* @param buf Pointer to a buffer for storing the data
|
||||
* @param count Number of bytes to read
|
||||
* @param flags Flags
|
||||
* @retval Number of bytes read or an error value
|
||||
*/
|
||||
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);
|
||||
|
@ -550,6 +560,7 @@ extern const struct sc_card_driver *sc_get_emv_driver(void);
|
|||
extern const struct sc_card_driver *sc_get_setec_driver(void);
|
||||
extern const struct sc_card_driver *sc_get_flex_driver(void);
|
||||
extern const struct sc_card_driver *sc_get_gpk_driver(void);
|
||||
extern const struct sc_card_driver *sc_get_tcos_driver(void);
|
||||
extern const struct sc_card_driver *sc_get_default_driver(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -185,6 +185,9 @@ int sc_establish_context(struct sc_context **ctx_out)
|
|||
#if 1
|
||||
ctx->card_drivers[i++] = sc_get_emv_driver();
|
||||
#endif
|
||||
#if 1
|
||||
ctx->card_drivers[i++] = sc_get_tcos_driver();
|
||||
#endif
|
||||
#if 1 && defined(HAVE_OPENSSL)
|
||||
ctx->card_drivers[i++] = sc_get_gpk_driver();
|
||||
#endif
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
#include "util.h"
|
||||
|
||||
int opt_reader = 0;
|
||||
int opt_reader = 0, opt_debug = 0;
|
||||
const char *opt_driver = NULL;
|
||||
|
||||
struct sc_file current_file;
|
||||
|
@ -41,12 +41,14 @@ struct sc_card *card = NULL;
|
|||
|
||||
const struct option options[] = {
|
||||
{ "reader", 1, 0, 'r' },
|
||||
{ "card-driver", 1, 0, 'd' },
|
||||
{ "card-driver", 1, 0, 'D' },
|
||||
{ "debug", 0, 0, 'd' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
const char *option_help[] = {
|
||||
"Uses reader number <arg> [0]",
|
||||
"Forces the use of driver <arg> [auto-detect]"
|
||||
"Forces the use of driver <arg> [auto-detect]",
|
||||
"Debug output -- maybe supplied several times",
|
||||
};
|
||||
|
||||
#define CMD_LS 0
|
||||
|
@ -103,17 +105,28 @@ void check_ret(int r, int op, const char *err, const struct sc_file *file)
|
|||
|
||||
int arg_to_path(const char *arg, struct sc_path *path)
|
||||
{
|
||||
char buf[6];
|
||||
int buf[2];
|
||||
u8 cbuf[2];
|
||||
|
||||
if (strlen(arg) != 4) {
|
||||
printf("Wrong ID length.\n");
|
||||
return -1;
|
||||
}
|
||||
strcpy(buf, "I");
|
||||
strcat(buf, arg);
|
||||
sc_format_path(buf, path);
|
||||
if (path->len != 2)
|
||||
if (sscanf(arg, "%02X%02X", &buf[0], &buf[1]) != 2) {
|
||||
printf("Invalid ID.\n");
|
||||
return -1;
|
||||
}
|
||||
cbuf[0] = buf[0];
|
||||
cbuf[1] = buf[1];
|
||||
if (cbuf[0] == 0x3F && cbuf[1] == 0x00) {
|
||||
path->len = 2;
|
||||
memcpy(path->value, cbuf, 2);
|
||||
path->type = SC_PATH_TYPE_PATH;
|
||||
} else {
|
||||
*path = current_path;
|
||||
sc_append_path_id(path, cbuf, 2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -170,9 +183,8 @@ int do_ls()
|
|||
struct sc_path path;
|
||||
struct sc_file file;
|
||||
|
||||
memcpy(path.value, cur, 2);
|
||||
path.len = 2;
|
||||
path.type = SC_PATH_TYPE_FILE_ID;
|
||||
path = current_path;
|
||||
sc_append_path_id(&path, cur, 2);
|
||||
r = sc_select_file(card, &path, &file);
|
||||
if (r) {
|
||||
check_ret(r, SC_AC_OP_SELECT, "unable to select file", ¤t_file);
|
||||
|
@ -230,8 +242,7 @@ int do_cd(const char *arg)
|
|||
}
|
||||
return -1;
|
||||
}
|
||||
memcpy(current_path.value + current_path.len, path.value, path.len);
|
||||
current_path.len += path.len;
|
||||
current_path = path;
|
||||
current_file = file;
|
||||
|
||||
return 0;
|
||||
|
@ -269,7 +280,7 @@ int read_and_print_record_file(struct sc_file *file)
|
|||
u8 buf[256];
|
||||
int rec, r;
|
||||
|
||||
for (rec = 0; ; rec++) {
|
||||
for (rec = 1; ; rec++) {
|
||||
r = sc_read_record(card, rec, buf, sizeof(buf), SC_READ_RECORD_BY_REC_NR);
|
||||
if (r == SC_ERROR_RECORD_NOT_FOUND)
|
||||
return 0;
|
||||
|
@ -333,20 +344,15 @@ int do_info(const char *arg)
|
|||
file = current_file;
|
||||
not_current = 0;
|
||||
} else {
|
||||
struct sc_path tmppath;
|
||||
|
||||
if (arg_to_path(arg, &tmppath) != 0) {
|
||||
if (arg_to_path(arg, &path) != 0) {
|
||||
printf("Usage: info [file_id]\n");
|
||||
return -1;
|
||||
}
|
||||
r = sc_select_file(card, &tmppath, &file);
|
||||
r = sc_select_file(card, &path, &file);
|
||||
if (r) {
|
||||
printf("unable to select file: %s\n", sc_strerror(r));
|
||||
return -1;
|
||||
}
|
||||
path = current_path;
|
||||
memcpy(path.value + path.len, tmppath.value, 2);
|
||||
path.len += 2;
|
||||
}
|
||||
switch (file.type) {
|
||||
case SC_FILE_TYPE_WORKING_EF:
|
||||
|
@ -411,6 +417,12 @@ int do_info(const char *arg)
|
|||
printf("%02X ", file.prop_attr[i]);
|
||||
printf("\n");
|
||||
}
|
||||
if (file.sec_attr_len) {
|
||||
printf("%-25s", "Security attributes:");
|
||||
for (i = 0; i < file.sec_attr_len; i++)
|
||||
printf("%02X ", file.sec_attr[i]);
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
if (not_current) {
|
||||
r = sc_select_file(card, ¤t_path, NULL);
|
||||
|
@ -815,7 +827,7 @@ int main(int argc, char * const argv[])
|
|||
printf("OpenSC Explorer version %s\n", sc_version);
|
||||
|
||||
while (1) {
|
||||
c = getopt_long(argc, argv, "r:c:", options, &long_optind);
|
||||
c = getopt_long(argc, argv, "r:c:d", options, &long_optind);
|
||||
if (c == -1)
|
||||
break;
|
||||
if (c == '?')
|
||||
|
@ -827,6 +839,9 @@ int main(int argc, char * const argv[])
|
|||
case 'c':
|
||||
opt_driver = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
opt_debug++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
r = sc_establish_context(&ctx);
|
||||
|
@ -834,6 +849,11 @@ int main(int argc, char * const argv[])
|
|||
fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r));
|
||||
return 1;
|
||||
}
|
||||
if (opt_debug) {
|
||||
ctx->error_file = stderr;
|
||||
ctx->debug_file = stdout;
|
||||
ctx->debug = opt_debug;
|
||||
}
|
||||
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;
|
||||
|
|
|
@ -259,6 +259,7 @@ int send_apdu(void)
|
|||
apdu.resplen = sizeof(rbuf);
|
||||
apdu.data = NULL;
|
||||
apdu.datalen = 0;
|
||||
apdu.lc = apdu.le = 0;
|
||||
len -= 4;
|
||||
if (len > 1) {
|
||||
apdu.lc = *p++;
|
||||
|
@ -274,6 +275,8 @@ int send_apdu(void)
|
|||
len -= apdu.lc;
|
||||
if (len) {
|
||||
apdu.le = *p++;
|
||||
if (apdu.le == 0)
|
||||
apdu.le = 256;
|
||||
len--;
|
||||
apdu.cse = SC_APDU_CASE_4_SHORT;
|
||||
} else
|
||||
|
@ -284,6 +287,8 @@ int send_apdu(void)
|
|||
}
|
||||
} else if (len == 1) {
|
||||
apdu.le = *p++;
|
||||
if (apdu.le == 0)
|
||||
apdu.le = 256;
|
||||
len--;
|
||||
apdu.cse = SC_APDU_CASE_2_SHORT;
|
||||
} else
|
||||
|
|
Loading…
Reference in New Issue