summary: - improve support for extended APDUs

- add experimental support for command chaining
         - simplify get_response prototype


git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@2673 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
nils 2005-10-30 20:37:03 +00:00
parent 95200e0f50
commit 43f0118740
3 changed files with 119 additions and 36 deletions

View File

@ -36,14 +36,21 @@ int sc_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2)
return card->ops->check_sw(card, sw1, sw2);
}
#define SC_IS_SHORT_APDU(a) ((a)->cse >= SC_APDU_CASE_1 && (a)->cse <= SC_APDU_CASE_4_SHORT)
#define SC_IS_EXT_APDU(a) ((a)->cse >= SC_APDU_CASE_2_EXT && (a)->cse <= SC_APDU_CASE_4_EXT)
static int sc_check_apdu(sc_context_t *ctx, const sc_apdu_t *apdu)
{
if (apdu->le > 256) {
sc_error(ctx, "Value of Le too big (maximum 256 bytes)\n");
if ((apdu->le > 256 && SC_IS_SHORT_APDU(apdu)) ||
(apdu->le > 65536 && SC_IS_EXT_APDU(apdu))) {
sc_error(ctx, "Value of Le too big '%lu' (maximum %d bytes)\n",
apdu->le, SC_IS_EXT_APDU(apdu) ? 65536 : 256);
SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
}
if (apdu->lc > 255) {
sc_error(ctx, "Value of Lc too big (maximum 255 bytes)\n");
if ((apdu->lc > 255 && SC_IS_SHORT_APDU(apdu)) ||
(apdu->lc > 65535 && SC_IS_EXT_APDU(apdu))){
sc_error(ctx, "Value of Lc too big '%lu' (maximum %d bytes)\n",
apdu->lc, SC_IS_EXT_APDU(apdu) ? 65535 : 255);
SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
}
switch (apdu->cse) {
@ -54,6 +61,7 @@ static int sc_check_apdu(sc_context_t *ctx, const sc_apdu_t *apdu)
}
break;
case SC_APDU_CASE_2_SHORT:
case SC_APDU_CASE_2_EXT:
if (apdu->datalen > 0) {
sc_error(ctx, "Case 2 APDU with data supplied\n");
SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
@ -68,12 +76,14 @@ static int sc_check_apdu(sc_context_t *ctx, const sc_apdu_t *apdu)
}
break;
case SC_APDU_CASE_3_SHORT:
case SC_APDU_CASE_3_EXT:
if (apdu->datalen == 0 || apdu->data == NULL) {
sc_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:
case SC_APDU_CASE_4_EXT:
if (apdu->datalen == 0 || apdu->data == NULL) {
sc_error(ctx, "Case 4 APDU with no data supplied\n");
SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
@ -87,9 +97,7 @@ static int sc_check_apdu(sc_context_t *ctx, const sc_apdu_t *apdu)
SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
}
break;
case SC_APDU_CASE_2_EXT:
case SC_APDU_CASE_3_EXT:
case SC_APDU_CASE_4_EXT:
default:
sc_error(ctx, "Invalid APDU case %d\n", apdu->cse);
SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
}
@ -171,9 +179,16 @@ static int sc_transceive(sc_card_t *card, sc_apdu_t *apdu)
*data++ = (u8) apdu->le;
break;
case SC_APDU_CASE_2_EXT:
*data++ = (u8) 0;
*data++ = (u8) (apdu->le >> 8);
*data++ = (u8) (apdu->le & 0xFF);
if (card->slot->active_protocol == SC_PROTO_T0) {
if (apdu->le >= 256)
*data++ = 0x00;
else
*data++ = apdu->le & 0xff;
} else {
*data++ = (u8) 0;
*data++ = (u8) (apdu->le >> 8);
*data++ = (u8) (apdu->le & 0xFF);
}
break;
case SC_APDU_CASE_3_SHORT:
*data++ = (u8) apdu->lc;
@ -192,6 +207,22 @@ static int sc_transceive(sc_card_t *card, sc_apdu_t *apdu)
/* unless T0 is used add Le byte */
*data++ = (u8) (apdu->le & 0xff);
break;
case SC_APDU_CASE_4_EXT:
if (card->slot->active_protocol == SC_PROTO_T0) {
/* currently there's no support for lc values > 255 */
*data++ = (u8) apdu->lc;
memcpy(data, apdu->data, data_bytes);
data += data_bytes;
} else {
*data++ = 0x00;
*data++ = (u8)(apdu->lc >> 8);
*data++ = (u8)(apdu->lc & 0xff);
memcpy(data, apdu->data, data_bytes);
data += data_bytes;
*data++ = (u8)(apdu->le >> 8);
*data++ = (u8)(apdu->le & 0xff);
}
break;
}
sendsize = data - sbuf;
#if 0
@ -286,31 +317,74 @@ int sc_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu)
}
}
if (apdu->sw1 == 0x61 && apdu->resplen == 0) {
size_t le;
if (orig_resplen == 0) {
apdu->sw1 = 0x90; /* FIXME: should we do this? */
apdu->sw2 = 0;
sc_unlock(card);
return 0;
}
le = apdu->sw2? (size_t) apdu->sw2 : 256;
size_t le, buflen = orig_resplen;
u8 *buf = apdu->resp;
if (card->ops->get_response == NULL) {
sc_unlock(card);
SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NOT_SUPPORTED);
}
r = card->ops->get_response(card, apdu, le);
if (r < 0) {
sc_unlock(card);
SC_FUNC_RETURN(card->ctx, 2, r);
}
sc_unlock(card);
sc_error(card->ctx, "errror: no GET RESPONSE command\n");
return SC_ERROR_NOT_SUPPORTED;
}
le = apdu->sw2 != 0 ? (size_t)apdu->sw2 : 256;
do {
if (buflen < le)
return SC_ERROR_WRONG_LENGTH;
r = card->ops->get_response(card, le, buf);
if (r < 0) {
sc_unlock(card);
SC_FUNC_RETURN(card->ctx, 2, r);
}
buf += le;
buflen -= le;
} while (r != 0);
/* we've read all data, let's return 0x9000 */
apdu->sw1 = 0x90;
apdu->sw2 = 0x00;
}
sc_unlock(card);
return 0;
}
int sc_chain_transmit_apdu(sc_card_t *card, sc_apdu_t *in_apdu)
{
int r = SC_ERROR_INTERNAL;
size_t len, plen;
u8 *buf;
sc_apdu_t apdu;
u8 rbuf[2];
if (card == NULL || in_apdu == NULL)
return SC_ERROR_INVALID_ARGUMENTS;
len = in_apdu->datalen;
buf = in_apdu->data;
while (len != 0) {
apdu = *in_apdu;
if (len > 255) {
plen = 255;
apdu.cla |= 0x10;
apdu.le = 2;
apdu.resplen = 2;
apdu.resp = rbuf;
} else
plen = len;
apdu.data = buf;
apdu.datalen = plen;
r = sc_transmit_apdu(card, &apdu);
if (r < 0)
return r;
len -= plen;
buf += plen;
}
return r;
}
void sc_format_apdu(sc_card_t *card, sc_apdu_t *apdu,
int cse, int ins, int p1, int p2)
{

View File

@ -564,15 +564,15 @@ static int iso7816_create_file(sc_card_t *card, sc_file_t *file)
return sc_check_sw(card, apdu.sw1, apdu.sw2);
}
static int iso7816_get_response(sc_card_t *card, sc_apdu_t *orig_apdu, size_t count)
static int iso7816_get_response(sc_card_t *card, size_t count, u8 *buf)
{
sc_apdu_t apdu;
int r;
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xC0, 0x00, 0x00);
apdu.le = count;
apdu.le = count;
apdu.resplen = count;
apdu.resp = orig_apdu->resp;
apdu.resp = buf;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
@ -583,11 +583,14 @@ static int iso7816_get_response(sc_card_t *card, sc_apdu_t *orig_apdu, size_t co
SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_WRONG_LENGTH);
}
orig_apdu->resplen = apdu.resplen;
orig_apdu->sw1 = 0x90;
orig_apdu->sw2 = 0x00;
if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)
r = 0; /* no more data to read */
else if (apdu.sw1 == 0x61)
r = apdu.sw2 == 0 ? 256 : apdu.sw2; /* more data to read */
else
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_FUNC_RETURN(card->ctx, 3, apdu.resplen);
return r;
}
static int iso7816_delete_file(sc_card_t *card, const sc_path_t *path)

View File

@ -120,6 +120,7 @@ extern "C" {
#define SC_MAX_SLOTS 4
#define SC_MAX_CARD_APPS 8
#define SC_MAX_APDU_BUFFER_SIZE 258
#define SC_MAX_EXT_APDU_BUFFER_SIZE 65538
#define SC_MAX_PIN_SIZE 256 /* OpenPGP card has 254 max */
#define SC_MAX_ATR_SIZE 33
#define SC_MAX_AID_SIZE 16
@ -543,7 +544,7 @@ struct sc_card_operations {
* <file>, if not NULL. */
int (*select_file)(struct sc_card *card, const struct sc_path *path,
struct sc_file **file_out);
int (*get_response)(struct sc_card *card, sc_apdu_t *orig_apdu, size_t count);
int (*get_response)(struct sc_card *card, size_t count, u8 *buf);
int (*get_challenge)(struct sc_card *card, u8 * buf, size_t count);
/*
@ -655,6 +656,11 @@ typedef struct sc_context {
/* APDU handling functions */
int sc_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu);
/* sends an APDU to the reader using command chaining if necessary
* XXX: this is only a temporary function, it's very likely that it's
* functionality will be included in sc_transmit_apdu very soon */
int sc_chain_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu);
void sc_format_apdu(sc_card_t *card, sc_apdu_t *apdu, int cse, int ins,
int p1, int p2);