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:
parent
95200e0f50
commit
43f0118740
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue