diff --git a/src/libopensc/iso7816.c b/src/libopensc/iso7816.c index 600d3dc5..4defc7f0 100644 --- a/src/libopensc/iso7816.c +++ b/src/libopensc/iso7816.c @@ -29,6 +29,43 @@ #include "asn1.h" #include "iso7816.h" + +static void fixup_transceive_length(const struct sc_card *card, + struct sc_apdu *apdu) +{ + size_t max_send_size; + size_t max_recv_size; + + assert(card != NULL && apdu != NULL); + + max_send_size = card->max_send_size; + max_recv_size = card->max_recv_size; + + if (card->caps & SC_CARD_CAP_APDU_EXT) { + if (!max_send_size) + max_send_size = 65535; + if (!max_recv_size) + max_recv_size = 65536; + } else { + if (!max_send_size) + max_send_size = 255; + if (!max_recv_size) + max_recv_size = 256; + } + + if (apdu->lc > max_send_size) { + /* The lower layers will automatically do chaining */ + apdu->flags |= SC_APDU_FLAGS_CHAINING; + } + + if (apdu->le > max_recv_size) { + /* The lower layers will automatically do a GET RESPONSE, if possible. + * All other workarounds must be carried out by the upper layers. */ + apdu->le = max_recv_size; + } +} + + static const struct sc_card_error iso7816_errors[] = { { 0x6200, SC_ERROR_CARD_CMD_FAILED, "Warning: no information given, non-volatile memory is unchanged" }, { 0x6281, SC_ERROR_CORRUPTED_DATA, "Part of returned data may be corrupted" }, @@ -121,12 +158,12 @@ iso7816_read_binary(struct sc_card *card, unsigned int idx, u8 *buf, size_t coun return SC_ERROR_OFFSET_TOO_LARGE; } - assert(count <= (card->max_recv_size > 0 ? card->max_recv_size : 256)); sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0xB0, (idx >> 8) & 0x7F, idx & 0xFF); apdu.le = count; apdu.resplen = count; apdu.resp = buf; + fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, r, "APDU transmit failed"); if (apdu.resplen == 0) @@ -166,6 +203,7 @@ iso7816_read_record(struct sc_card *card, apdu.resplen = count; apdu.resp = buf; + fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.resplen == 0) @@ -191,6 +229,7 @@ iso7816_write_record(struct sc_card *card, unsigned int rec_nr, apdu.datalen = count; apdu.data = buf; + fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); @@ -214,6 +253,7 @@ iso7816_append_record(struct sc_card *card, apdu.datalen = count; apdu.data = buf; + fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); @@ -239,6 +279,7 @@ iso7816_update_record(struct sc_card *card, unsigned int rec_nr, apdu.datalen = count; apdu.data = buf; + fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); @@ -255,8 +296,6 @@ iso7816_write_binary(struct sc_card *card, struct sc_apdu apdu; int r; - assert(count <= (card->max_send_size > 0 ? card->max_send_size : 255)); - if (idx > 0x7fff) { sc_log(card->ctx, "invalid EF offset: 0x%X > 0x7FFF", idx); return SC_ERROR_OFFSET_TOO_LARGE; @@ -268,6 +307,7 @@ iso7816_write_binary(struct sc_card *card, apdu.datalen = count; apdu.data = buf; + fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); @@ -284,8 +324,6 @@ iso7816_update_binary(struct sc_card *card, struct sc_apdu apdu; int r; - assert(count <= (card->max_send_size > 0 ? card->max_send_size : 255)); - if (idx > 0x7fff) { sc_log(card->ctx, "invalid EF offset: 0x%X > 0x7FFF", idx); return SC_ERROR_OFFSET_TOO_LARGE; @@ -296,6 +334,7 @@ iso7816_update_binary(struct sc_card *card, apdu.datalen = count; apdu.data = buf; + fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); @@ -829,6 +868,8 @@ iso7816_compute_signature(struct sc_card *card, apdu.data = data; apdu.lc = datalen; apdu.datalen = datalen; + + fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { @@ -864,19 +905,15 @@ iso7816_decipher(struct sc_card *card, sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x80, 0x86); apdu.resp = out; apdu.resplen = outlen; - /* if less than 256 bytes are expected than set Le to 0x00 - * to tell the card the we want everything available (note: we - * always have Le <= crgram_len) */ - apdu.le = (outlen >= 256 && crgram_len < 256) ? 256 : outlen; - /* Use APDU chaining with 2048bit RSA keys if the card does not do extended APDU-s */ - if ((crgram_len+1 > 255) && !(card->caps & SC_CARD_CAP_APDU_EXT)) - apdu.flags |= SC_APDU_FLAGS_CHAINING; + apdu.le = outlen; sbuf[0] = 0; /* padding indicator byte, 0x00 = No further indication */ memcpy(sbuf + 1, crgram, crgram_len); apdu.data = sbuf; apdu.lc = crgram_len + 1; apdu.datalen = crgram_len + 1; + + fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); sc_mem_clear(sbuf, crgram_len + 1); free(sbuf);