aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--scd/apdu.c6
-rw-r--r--scd/app-common.h8
-rw-r--r--scd/ccid-driver.c156
3 files changed, 87 insertions, 83 deletions
diff --git a/scd/apdu.c b/scd/apdu.c
index 53cc4b9b3..f6cca8c7b 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -3806,9 +3806,9 @@ send_le (int slot, int class, int ins, int p0, int p1,
apdu[apdulen++] = ins;
apdu[apdulen++] = p0;
apdu[apdulen++] = p1;
- apdu[apdulen++] = 0; /* Z byte: Extended length marker. */
- if (lc >= 0)
+ if (lc > 0)
{
+ apdu[apdulen++] = 0; /* Z byte: Extended length marker. */
apdu[apdulen++] = ((lc >> 8) & 0xff);
apdu[apdulen++] = (lc & 0xff);
memcpy (apdu+apdulen, data, lc);
@@ -3817,6 +3817,8 @@ send_le (int slot, int class, int ins, int p0, int p1,
}
if (le != -1)
{
+ if (lc <= 0)
+ apdu[apdulen++] = 0; /* Z byte: Extended length marker. */
apdu[apdulen++] = ((le >> 8) & 0xff);
apdu[apdulen++] = (le & 0xff);
}
diff --git a/scd/app-common.h b/scd/app-common.h
index 50046a4e3..379bcd18d 100644
--- a/scd/app-common.h
+++ b/scd/app-common.h
@@ -67,10 +67,10 @@ struct app_ctx_s {
size_t serialnolen; /* Length in octets of serialnumber. */
const char *apptype;
unsigned int card_version;
- int did_chv1;
- int force_chv1; /* True if the card does not cache CHV1. */
- int did_chv2;
- int did_chv3;
+ unsigned int did_chv1:1;
+ unsigned int force_chv1:1; /* True if the card does not cache CHV1. */
+ unsigned int did_chv2:1;
+ unsigned int did_chv3:1;
struct app_local_s *app_local; /* Local to the application. */
struct {
void (*deinit) (app_t app);
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
index 1926f7115..151b371b4 100644
--- a/scd/ccid-driver.c
+++ b/scd/ccid-driver.c
@@ -96,6 +96,16 @@
#define DRVNAME "ccid-driver: "
+/* Max length of buffer with out CCID message header of 10-byte
+ Sending: 547 for RSA-4096 key import
+ APDU size = 540 (24+4+256+256)
+ commnd + lc + le = 4 + 3 + 0
+ Sending: write data object of cardholder certificate
+ APDU size = 2048
+ commnd + lc + le = 4 + 3 + 0
+ Receiving: 2048 for cardholder certificate
+*/
+#define CCID_MAX_BUF (2048+7+10)
/* Depending on how this source is used we either define our error
output to go to stderr or to the jnlib based logging functions. We
@@ -242,7 +252,7 @@ struct ccid_driver_s
unsigned char t1_nr;
unsigned char nonnull_nad;
int max_ifsd;
- int ifsd;
+ int max_ccid_msglen;
int ifsc;
unsigned char apdu_level:2; /* Reader supports short APDU level
exchange. With a value of 2 short
@@ -711,7 +721,7 @@ prepare_special_transport (ccid_driver_t handle)
handle->nonnull_nad = 0;
handle->auto_ifsd = 0;
handle->max_ifsd = 32;
- handle->ifsd = 0;
+ handle->max_ccid_msglen = CCID_MAX_BUF;
handle->has_pinpad = 0;
handle->apdu_level = 0;
switch (handle->id_product)
@@ -743,7 +753,6 @@ parse_ccid_descriptor (ccid_driver_t handle,
handle->nonnull_nad = 0;
handle->auto_ifsd = 0;
handle->max_ifsd = 32;
- handle->ifsd = 0;
handle->has_pinpad = 0;
handle->apdu_level = 0;
handle->auto_voltage = 0;
@@ -884,6 +893,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
us = convert_le_u32(buf+44);
DEBUGOUT_1 (" dwMaxCCIDMsgLen %5u\n", us);
+ handle->max_ccid_msglen = us;
DEBUGOUT ( " bClassGetResponse ");
if (buf[48] == 0xff)
@@ -2794,109 +2804,101 @@ is_exlen_apdu (const unsigned char *apdu, size_t apdulen)
/* Helper for ccid_transceive used for APDU level exchanges. */
static int
ccid_transceive_apdu_level (ccid_driver_t handle,
- const unsigned char *apdu_buf, size_t apdu_buflen,
+ const unsigned char *apdu_buf, size_t apdu_len,
unsigned char *resp, size_t maxresplen,
size_t *nresp)
{
int rc;
- unsigned char send_buffer[10+261+300], recv_buffer[10+261+300];
- const unsigned char *apdu;
- size_t apdulen;
- unsigned char *msg;
+ unsigned char msg[CCID_MAX_BUF];
+ const unsigned char *apdu_p;
+ size_t apdu_part_len;
size_t msglen;
unsigned char seqno;
int bwi = 4;
+ unsigned char chain = 0;
- msg = send_buffer;
+ if (apdu_len == 0 || apdu_len > sizeof (msg) - 10)
+ return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
- apdu = apdu_buf;
- apdulen = apdu_buflen;
- assert (apdulen);
+ apdu_p = apdu_buf;
+ while (1)
+ {
+ apdu_part_len = apdu_len;
+ if (apdu_part_len > handle->max_ccid_msglen - 10)
+ {
+ apdu_part_len = handle->max_ccid_msglen - 10;
+ chain |= 0x01;
+ }
- /* The maximum length for a short APDU T=1 block is 261. For an
- extended APDU T=1 block the maximum length 65544; however
- extended APDU exchange level is not fully supported yet. */
- if (apdulen > sizeof (send_buffer) - 10)
- return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
+ msg[0] = PC_to_RDR_XfrBlock;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = bwi;
+ msg[8] = chain;
+ msg[9] = 0;
+ memcpy (msg+10, apdu_p, apdu_part_len);
+ set_msg_len (msg, apdu_part_len);
+ msglen = 10 + apdu_part_len;
- msg[0] = PC_to_RDR_XfrBlock;
- msg[5] = 0; /* slot */
- msg[6] = seqno = handle->seqno++;
- msg[7] = bwi; /* bBWI */
- msg[8] = 0; /* RFU */
- msg[9] = 0; /* RFU */
- memcpy (msg+10, apdu, apdulen);
- set_msg_len (msg, apdulen);
- msglen = 10 + apdulen;
+ rc = bulk_out (handle, msg, msglen, 0);
+ if (rc)
+ return rc;
- rc = bulk_out (handle, msg, msglen, 0);
- if (rc)
- return rc;
+ apdu_p += apdu_part_len;
+ apdu_len -= apdu_part_len;
- msg = recv_buffer;
- rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
- RDR_to_PC_DataBlock, seqno, 5000, 0);
- if (rc)
- return rc;
+ rc = bulk_in (handle, msg, sizeof msg, &msglen,
+ RDR_to_PC_DataBlock, seqno, 5000, 0);
+ if (rc)
+ return rc;
+
+ if (!(chain & 0x01))
+ break;
- if (msg[9] == 1)
+ chain = 0x02;
+ }
+
+ apdu_len = 0;
+ while (1)
{
- size_t total_msglen = msglen;
+ apdu_part_len = msglen - 10;
+ if (resp && apdu_len + apdu_part_len <= maxresplen)
+ memcpy (resp + apdu_len, msg+10, apdu_part_len);
+ apdu_len += apdu_part_len;
- while (1)
- {
- unsigned char status;
+ if (!(msg[9] & 0x01))
+ break;
- msg = recv_buffer + total_msglen;
+ msg[0] = PC_to_RDR_XfrBlock;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = bwi;
+ msg[8] = 0x10; /* Request next data block */
+ msg[9] = 0;
+ set_msg_len (msg, 0);
+ msglen = 10;
- msg[0] = PC_to_RDR_XfrBlock;
- msg[5] = 0; /* slot */
- msg[6] = seqno = handle->seqno++;
- msg[7] = bwi; /* bBWI */
- msg[8] = 0x10; /* Request next data block */
- msg[9] = 0;
- set_msg_len (msg, 0);
- msglen = 10;
-
- rc = bulk_out (handle, msg, msglen, 0);
- if (rc)
- return rc;
-
- rc = bulk_in (handle, msg, sizeof recv_buffer - total_msglen, &msglen,
- RDR_to_PC_DataBlock, seqno, 5000, 0);
- if (rc)
- return rc;
- status = msg[9];
- memmove (msg, msg+10, msglen - 10);
- total_msglen += msglen - 10;
- if (total_msglen >= sizeof recv_buffer)
- return CCID_DRIVER_ERR_OUT_OF_CORE;
-
- if (status == 0x02)
- break;
- }
+ rc = bulk_out (handle, msg, msglen, 0);
+ if (rc)
+ return rc;
- apdu = recv_buffer + 10;
- apdulen = total_msglen - 10;
- }
- else
- {
- apdu = msg + 10;
- apdulen = msglen - 10;
+ rc = bulk_in (handle, msg, sizeof msg, &msglen,
+ RDR_to_PC_DataBlock, seqno, 5000, 0);
+ if (rc)
+ return rc;
}
if (resp)
{
- if (apdulen > maxresplen)
+ if (apdu_len > maxresplen)
{
DEBUGOUT_2 ("provided buffer too short for received data "
"(%u/%u)\n",
- (unsigned int)apdulen, (unsigned int)maxresplen);
+ (unsigned int)apdu_len, (unsigned int)maxresplen);
return CCID_DRIVER_ERR_INV_VALUE;
}
- memcpy (resp, apdu, apdulen);
- *nresp = apdulen;
+ *nresp = apdu_len;
}
return 0;