1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-06-13 00:09:51 +02:00

scd: better handling of extended APDU.

* scd/apdu.c (send_le): Bug fix for not append Z when lc<0&&le<0.
* scd/app-common.h (struct app_ctx_s): Use bit fields for flags.
* scd/ccid-driver.c (CCID_MAX_BUF): New.  Only for OpenPGPcard.
(struct ccid_driver_s): New field of max_ccid_msglen.
 Remove ifsd field.
(parse_ccid_descriptor): Initialize max_ccid_msglen.
(ccid_transceive_apdu_level): Implement sending extended APDU in
chain of CCID message.

--

With this patch, we won't need PC/SC library/service any more.
GnuPG-bug-id: 1947
This commit is contained in:
NIIBE Yutaka 2015-04-14 14:17:03 +09:00
parent 25fce93ba1
commit 971d558e86
3 changed files with 90 additions and 86 deletions

View File

@ -3806,9 +3806,9 @@ send_le (int slot, int class, int ins, int p0, int p1,
apdu[apdulen++] = ins; apdu[apdulen++] = ins;
apdu[apdulen++] = p0; apdu[apdulen++] = p0;
apdu[apdulen++] = p1; 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 >> 8) & 0xff);
apdu[apdulen++] = (lc & 0xff); apdu[apdulen++] = (lc & 0xff);
memcpy (apdu+apdulen, data, lc); 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 (le != -1)
{ {
if (lc <= 0)
apdu[apdulen++] = 0; /* Z byte: Extended length marker. */
apdu[apdulen++] = ((le >> 8) & 0xff); apdu[apdulen++] = ((le >> 8) & 0xff);
apdu[apdulen++] = (le & 0xff); apdu[apdulen++] = (le & 0xff);
} }

View File

@ -67,10 +67,10 @@ struct app_ctx_s {
size_t serialnolen; /* Length in octets of serialnumber. */ size_t serialnolen; /* Length in octets of serialnumber. */
const char *apptype; const char *apptype;
unsigned int card_version; unsigned int card_version;
int did_chv1; unsigned int did_chv1:1;
int force_chv1; /* True if the card does not cache CHV1. */ unsigned int force_chv1:1; /* True if the card does not cache CHV1. */
int did_chv2; unsigned int did_chv2:1;
int did_chv3; unsigned int did_chv3:1;
struct app_local_s *app_local; /* Local to the application. */ struct app_local_s *app_local; /* Local to the application. */
struct { struct {
void (*deinit) (app_t app); void (*deinit) (app_t app);

View File

@ -96,6 +96,16 @@
#define DRVNAME "ccid-driver: " #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 /* 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 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 t1_nr;
unsigned char nonnull_nad; unsigned char nonnull_nad;
int max_ifsd; int max_ifsd;
int ifsd; int max_ccid_msglen;
int ifsc; int ifsc;
unsigned char apdu_level:2; /* Reader supports short APDU level unsigned char apdu_level:2; /* Reader supports short APDU level
exchange. With a value of 2 short exchange. With a value of 2 short
@ -711,7 +721,7 @@ prepare_special_transport (ccid_driver_t handle)
handle->nonnull_nad = 0; handle->nonnull_nad = 0;
handle->auto_ifsd = 0; handle->auto_ifsd = 0;
handle->max_ifsd = 32; handle->max_ifsd = 32;
handle->ifsd = 0; handle->max_ccid_msglen = CCID_MAX_BUF;
handle->has_pinpad = 0; handle->has_pinpad = 0;
handle->apdu_level = 0; handle->apdu_level = 0;
switch (handle->id_product) switch (handle->id_product)
@ -743,7 +753,6 @@ parse_ccid_descriptor (ccid_driver_t handle,
handle->nonnull_nad = 0; handle->nonnull_nad = 0;
handle->auto_ifsd = 0; handle->auto_ifsd = 0;
handle->max_ifsd = 32; handle->max_ifsd = 32;
handle->ifsd = 0;
handle->has_pinpad = 0; handle->has_pinpad = 0;
handle->apdu_level = 0; handle->apdu_level = 0;
handle->auto_voltage = 0; handle->auto_voltage = 0;
@ -884,6 +893,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
us = convert_le_u32(buf+44); us = convert_le_u32(buf+44);
DEBUGOUT_1 (" dwMaxCCIDMsgLen %5u\n", us); DEBUGOUT_1 (" dwMaxCCIDMsgLen %5u\n", us);
handle->max_ccid_msglen = us;
DEBUGOUT ( " bClassGetResponse "); DEBUGOUT ( " bClassGetResponse ");
if (buf[48] == 0xff) 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. */ /* Helper for ccid_transceive used for APDU level exchanges. */
static int static int
ccid_transceive_apdu_level (ccid_driver_t handle, 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, unsigned char *resp, size_t maxresplen,
size_t *nresp) size_t *nresp)
{ {
int rc; int rc;
unsigned char send_buffer[10+261+300], recv_buffer[10+261+300]; unsigned char msg[CCID_MAX_BUF];
const unsigned char *apdu; const unsigned char *apdu_p;
size_t apdulen; size_t apdu_part_len;
unsigned char *msg;
size_t msglen; size_t msglen;
unsigned char seqno; unsigned char seqno;
int bwi = 4; int bwi = 4;
unsigned char chain = 0;
msg = send_buffer; if (apdu_len == 0 || apdu_len > sizeof (msg) - 10)
apdu = apdu_buf;
apdulen = apdu_buflen;
assert (apdulen);
/* 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. */ return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
msg[0] = PC_to_RDR_XfrBlock; apdu_p = apdu_buf;
msg[5] = 0; /* slot */ while (1)
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;
msg = recv_buffer;
rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
RDR_to_PC_DataBlock, seqno, 5000, 0);
if (rc)
return rc;
if (msg[9] == 1)
{ {
size_t total_msglen = msglen; apdu_part_len = apdu_len;
if (apdu_part_len > handle->max_ccid_msglen - 10)
while (1)
{ {
unsigned char status; apdu_part_len = handle->max_ccid_msglen - 10;
chain |= 0x01;
msg = recv_buffer + total_msglen;
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;
} }
apdu = recv_buffer + 10; msg[0] = PC_to_RDR_XfrBlock;
apdulen = total_msglen - 10; 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;
rc = bulk_out (handle, msg, msglen, 0);
if (rc)
return rc;
apdu_p += apdu_part_len;
apdu_len -= apdu_part_len;
rc = bulk_in (handle, msg, sizeof msg, &msglen,
RDR_to_PC_DataBlock, seqno, 5000, 0);
if (rc)
return rc;
if (!(chain & 0x01))
break;
chain = 0x02;
} }
else
apdu_len = 0;
while (1)
{ {
apdu = msg + 10; apdu_part_len = msglen - 10;
apdulen = msglen - 10; if (resp && apdu_len + apdu_part_len <= maxresplen)
memcpy (resp + apdu_len, msg+10, apdu_part_len);
apdu_len += apdu_part_len;
if (!(msg[9] & 0x01))
break;
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;
rc = bulk_out (handle, msg, msglen, 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 (resp) if (resp)
{ {
if (apdulen > maxresplen) if (apdu_len > maxresplen)
{ {
DEBUGOUT_2 ("provided buffer too short for received data " DEBUGOUT_2 ("provided buffer too short for received data "
"(%u/%u)\n", "(%u/%u)\n",
(unsigned int)apdulen, (unsigned int)maxresplen); (unsigned int)apdu_len, (unsigned int)maxresplen);
return CCID_DRIVER_ERR_INV_VALUE; return CCID_DRIVER_ERR_INV_VALUE;
} }
memcpy (resp, apdu, apdulen); *nresp = apdu_len;
*nresp = apdulen;
} }
return 0; return 0;