mirror of
git://git.gnupg.org/gnupg.git
synced 2025-02-01 16:33:02 +01:00
SCD: Support not-so-smart card readers.
* scd/ccid-driver.c (struct ccid_driver_s): Add auto_voltage, auto_param, and auto_pps. (parse_ccid_descriptor): Set auto_voltage, auto_param, and auto_pps. Support non-autoconf readers. (update_param_by_atr): New. (ccid_get_atr): Use 5V for PowerOn when auto_voltage is not supported. Use 0x10 when nonnull_nad for SetParameters. Call update_param_by_atr for parsing ATR, and use param for SetParameters. Send PPS if reader requires it and card is negotiable. When bNadValue in the return values of SetParameters == 0, clear handle->nonnull_nad flag. -- This change is to support more card readers by the internal driver. Tested with 08e6:3478 Gemplus PinPad Smart Card Reader.
This commit is contained in:
parent
bb51edc31e
commit
a776f66036
@ -264,6 +264,9 @@ struct ccid_driver_s
|
||||
unsigned char apdu_level:2; /* Reader supports short APDU level
|
||||
exchange. With a value of 2 short
|
||||
and extended level is supported.*/
|
||||
unsigned int auto_voltage:1;
|
||||
unsigned int auto_param:1;
|
||||
unsigned int auto_pps:1;
|
||||
unsigned int auto_ifsd:1;
|
||||
unsigned int powered_off:1;
|
||||
unsigned int has_pinpad:2;
|
||||
@ -758,7 +761,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int us;
|
||||
int have_t1 = 0, have_tpdu=0, have_auto_conf = 0;
|
||||
int have_t1 = 0, have_tpdu=0;
|
||||
|
||||
|
||||
handle->nonnull_nad = 0;
|
||||
@ -767,6 +770,9 @@ parse_ccid_descriptor (ccid_driver_t handle,
|
||||
handle->ifsd = 0;
|
||||
handle->has_pinpad = 0;
|
||||
handle->apdu_level = 0;
|
||||
handle->auto_voltage = 0;
|
||||
handle->auto_param = 0;
|
||||
handle->auto_pps = 0;
|
||||
DEBUGOUT_3 ("idVendor: %04X idProduct: %04X bcdDevice: %04X\n",
|
||||
handle->id_vendor, handle->id_product, handle->bcd_device);
|
||||
if (buflen < 54 || buf[0] < 54)
|
||||
@ -842,22 +848,31 @@ parse_ccid_descriptor (ccid_driver_t handle,
|
||||
DEBUGOUT_1 (" dwFeatures %08X\n", us);
|
||||
if ((us & 0x0002))
|
||||
{
|
||||
DEBUGOUT (" Auto configuration based on ATR\n");
|
||||
have_auto_conf = 1;
|
||||
DEBUGOUT (" Auto configuration based on ATR (assumes auto voltage)\n");
|
||||
handle->auto_voltage = 1;
|
||||
}
|
||||
if ((us & 0x0004))
|
||||
DEBUGOUT (" Auto activation on insert\n");
|
||||
if ((us & 0x0008))
|
||||
DEBUGOUT (" Auto voltage selection\n");
|
||||
{
|
||||
DEBUGOUT (" Auto voltage selection\n");
|
||||
handle->auto_voltage = 1;
|
||||
}
|
||||
if ((us & 0x0010))
|
||||
DEBUGOUT (" Auto clock change\n");
|
||||
if ((us & 0x0020))
|
||||
DEBUGOUT (" Auto baud rate change\n");
|
||||
if ((us & 0x0040))
|
||||
DEBUGOUT (" Auto parameter negotiation made by CCID\n");
|
||||
{
|
||||
DEBUGOUT (" Auto parameter negotiation made by CCID\n");
|
||||
handle->auto_param = 1;
|
||||
}
|
||||
else if ((us & 0x0080))
|
||||
DEBUGOUT (" Auto PPS made by CCID\n");
|
||||
else if ((us & (0x0040 | 0x0080)))
|
||||
{
|
||||
DEBUGOUT (" Auto PPS made by CCID\n");
|
||||
handle->auto_pps = 1;
|
||||
}
|
||||
if ((us & (0x0040 | 0x0080)) == (0x0040 | 0x0080))
|
||||
DEBUGOUT (" WARNING: conflicting negotiation features\n");
|
||||
|
||||
if ((us & 0x0100))
|
||||
@ -935,11 +950,10 @@ parse_ccid_descriptor (ccid_driver_t handle,
|
||||
DEBUGOUT_LF ();
|
||||
}
|
||||
|
||||
if (!have_t1 || !(have_tpdu || handle->apdu_level) || !have_auto_conf)
|
||||
if (!have_t1 || !(have_tpdu || handle->apdu_level))
|
||||
{
|
||||
DEBUGOUT ("this drivers requires that the reader supports T=1, "
|
||||
"TPDU or APDU level exchange and auto configuration - "
|
||||
"this is not available\n");
|
||||
"TPDU or APDU level exchange - this is not available\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -2338,6 +2352,151 @@ ccid_slot_status (ccid_driver_t handle, int *statusbits)
|
||||
}
|
||||
|
||||
|
||||
/* Parse ATR string (of ATRLEN) and update parameters at PARAM.
|
||||
Calling this routine, it should prepare default values at PARAM
|
||||
beforehand. This routine assumes that card is accessed by T=1
|
||||
protocol. It doesn't analyze historical bytes at all.
|
||||
|
||||
Returns < 0 value on error:
|
||||
-1 for parse error or integrity check error
|
||||
-2 for card doesn't support T=1 protocol
|
||||
-3 for parameters are nod explicitly defined by ATR
|
||||
-4 for this driver doesn't support CRC
|
||||
|
||||
Returns >= 0 on success:
|
||||
0 for card is negotiable mode
|
||||
1 for card is specific mode (and not negotiable)
|
||||
*/
|
||||
static int
|
||||
update_param_by_atr (unsigned char *param, unsigned char *atr, size_t atrlen)
|
||||
{
|
||||
int i = -1;
|
||||
int t, y, chk;
|
||||
int historical_bytes_num, negotiable = 1;
|
||||
|
||||
#define NEXTBYTE() do { i++; if (atrlen <= i) return -1; } while (0)
|
||||
|
||||
NEXTBYTE ();
|
||||
|
||||
if (atr[i] == 0x3F)
|
||||
param[1] |= 0x02; /* Convention is inverse. */
|
||||
NEXTBYTE ();
|
||||
|
||||
y = (atr[i] >> 4);
|
||||
historical_bytes_num = atr[i] & 0x0f;
|
||||
NEXTBYTE ();
|
||||
|
||||
if ((y & 1))
|
||||
{
|
||||
param[0] = atr[i]; /* TA1 - Fi & Di */
|
||||
NEXTBYTE ();
|
||||
}
|
||||
|
||||
if ((y & 2))
|
||||
NEXTBYTE (); /* TB1 - ignore */
|
||||
|
||||
if ((y & 4))
|
||||
{
|
||||
param[2] = atr[i]; /* TC1 - Guard Time */
|
||||
NEXTBYTE ();
|
||||
}
|
||||
|
||||
if ((y & 8))
|
||||
{
|
||||
y = (atr[i] >> 4); /* TD1 */
|
||||
t = atr[i] & 0x0f;
|
||||
NEXTBYTE ();
|
||||
|
||||
if ((y & 1))
|
||||
{ /* TA2 - PPS mode */
|
||||
if ((atr[i] & 0x0f) != 1)
|
||||
return -2; /* Wrong card protocol (!= 1). */
|
||||
|
||||
if ((atr[i] & 0x10) != 0x10)
|
||||
return -3; /* Transmission parameters are implicitly defined. */
|
||||
|
||||
negotiable = 0; /* TA2 means specific mode. */
|
||||
NEXTBYTE ();
|
||||
}
|
||||
|
||||
if ((y & 2))
|
||||
NEXTBYTE (); /* TB2 - ignore */
|
||||
|
||||
if ((y & 4))
|
||||
NEXTBYTE (); /* TC2 - ignore */
|
||||
|
||||
if ((y & 8))
|
||||
{
|
||||
y = (atr[i] >> 4); /* TD2 */
|
||||
t = atr[i] & 0x0f;
|
||||
NEXTBYTE ();
|
||||
}
|
||||
else
|
||||
y = 0;
|
||||
|
||||
while (y)
|
||||
{
|
||||
if ((y & 1))
|
||||
{ /* TAx */
|
||||
if (t == 1)
|
||||
param[5] = atr[i]; /* IFSC */
|
||||
else if (t == 15)
|
||||
/* XXX: check voltage? */
|
||||
param[4] = (atr[i] >> 6); /* ClockStop */
|
||||
|
||||
NEXTBYTE ();
|
||||
}
|
||||
|
||||
if ((y & 2))
|
||||
{
|
||||
if (t == 1)
|
||||
param[3] = atr[i]; /* TBx - BWI & CWI */
|
||||
NEXTBYTE ();
|
||||
}
|
||||
|
||||
if ((y & 4))
|
||||
{
|
||||
if (t == 1)
|
||||
param[1] |= (atr[i] & 0x01); /* TCx - LRC/CRC */
|
||||
NEXTBYTE ();
|
||||
|
||||
if (param[1] & 0x01)
|
||||
return -4; /* CRC not supported yet. */
|
||||
}
|
||||
|
||||
if ((y & 8))
|
||||
{
|
||||
y = (atr[i] >> 4); /* TDx */
|
||||
t = atr[i] & 0x0f;
|
||||
NEXTBYTE ();
|
||||
}
|
||||
else
|
||||
y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
i += historical_bytes_num - 1;
|
||||
NEXTBYTE ();
|
||||
if (atrlen != i+1)
|
||||
return -1;
|
||||
|
||||
#undef NEXTBYTE
|
||||
|
||||
chk = 0;
|
||||
do
|
||||
{
|
||||
chk ^= atr[i];
|
||||
i--;
|
||||
}
|
||||
while (i > 0);
|
||||
|
||||
if (chk != 0)
|
||||
return -1;
|
||||
|
||||
return negotiable;
|
||||
}
|
||||
|
||||
|
||||
/* Return the ATR of the card. This is not a cached value and thus an
|
||||
actual reset is done. */
|
||||
int
|
||||
@ -2354,6 +2513,15 @@ ccid_get_atr (ccid_driver_t handle,
|
||||
unsigned int edc;
|
||||
int tried_iso = 0;
|
||||
int got_param;
|
||||
unsigned char param[7] = { /* For Protocol T=1 */
|
||||
0x11, /* bmFindexDindex */
|
||||
0x10, /* bmTCCKST1 */
|
||||
0x00, /* bGuardTimeT1 */
|
||||
0x4d, /* bmWaitingIntegersT1 */
|
||||
0x00, /* bClockStop */
|
||||
0x20, /* bIFSC */
|
||||
0x00 /* bNadValue */
|
||||
};
|
||||
|
||||
/* First check whether a card is available. */
|
||||
rc = ccid_slot_status (handle, &statusbits);
|
||||
@ -2368,7 +2536,8 @@ ccid_get_atr (ccid_driver_t handle,
|
||||
msg[0] = PC_to_RDR_IccPowerOn;
|
||||
msg[5] = 0; /* slot */
|
||||
msg[6] = seqno = handle->seqno++;
|
||||
msg[7] = 0; /* power select (0=auto, 1=5V, 2=3V, 3=1.8V) */
|
||||
/* power select (0=auto, 1=5V, 2=3V, 3=1.8V) */
|
||||
msg[7] = handle->auto_voltage ? 0 : 1;
|
||||
msg[8] = 0; /* RFU */
|
||||
msg[9] = 0; /* RFU */
|
||||
set_msg_len (msg, 0);
|
||||
@ -2410,23 +2579,73 @@ ccid_get_atr (ccid_driver_t handle,
|
||||
*atrlen = n;
|
||||
}
|
||||
|
||||
param[6] = handle->nonnull_nad? ((1 << 4) | 0): 0;
|
||||
rc = update_param_by_atr (param, msg+10, msglen - 10);
|
||||
if (rc < 0)
|
||||
{
|
||||
DEBUGOUT_1 ("update_param_by_atr failed: %d\n", rc);
|
||||
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
||||
}
|
||||
|
||||
got_param = 0;
|
||||
msg[0] = PC_to_RDR_GetParameters;
|
||||
msg[5] = 0; /* slot */
|
||||
msg[6] = seqno = handle->seqno++;
|
||||
msg[7] = 0; /* RFU */
|
||||
msg[8] = 0; /* RFU */
|
||||
msg[9] = 0; /* RFU */
|
||||
set_msg_len (msg, 0);
|
||||
msglen = 10;
|
||||
rc = bulk_out (handle, msg, msglen, 0);
|
||||
if (!rc)
|
||||
rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters,
|
||||
seqno, 2000, 0);
|
||||
if (rc)
|
||||
DEBUGOUT ("GetParameters failed\n");
|
||||
else if (msglen == 17 && msg[9] == 1)
|
||||
got_param = 1;
|
||||
|
||||
if (handle->auto_param)
|
||||
{
|
||||
msg[0] = PC_to_RDR_GetParameters;
|
||||
msg[5] = 0; /* slot */
|
||||
msg[6] = seqno = handle->seqno++;
|
||||
msg[7] = 0; /* RFU */
|
||||
msg[8] = 0; /* RFU */
|
||||
msg[9] = 0; /* RFU */
|
||||
set_msg_len (msg, 0);
|
||||
msglen = 10;
|
||||
rc = bulk_out (handle, msg, msglen, 0);
|
||||
if (!rc)
|
||||
rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters,
|
||||
seqno, 2000, 0);
|
||||
if (rc)
|
||||
DEBUGOUT ("GetParameters failed\n");
|
||||
else if (msglen == 17 && msg[9] == 1)
|
||||
got_param = 1;
|
||||
}
|
||||
else if (handle->auto_pps)
|
||||
;
|
||||
else if (rc == 1) /* It's negotiable, send PPS. */
|
||||
{
|
||||
msg[0] = PC_to_RDR_XfrBlock;
|
||||
msg[5] = 0; /* slot */
|
||||
msg[6] = seqno = handle->seqno++;
|
||||
msg[7] = 0;
|
||||
msg[8] = 0;
|
||||
msg[9] = 0;
|
||||
msg[10] = 0xff; /* PPSS */
|
||||
msg[11] = 0x11; /* PPS0: PPS1, Protocol T=1 */
|
||||
msg[12] = param[0]; /* PPS1: Fi / Di */
|
||||
msg[13] = 0xff ^ 0x11 ^ param[0]; /* PCK */
|
||||
set_msg_len (msg, 4);
|
||||
msglen = 10 + 4;
|
||||
|
||||
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 (msglen != 10 + 4)
|
||||
{
|
||||
DEBUGOUT_1 ("Setting PPS failed: %d\n", msglen);
|
||||
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
||||
}
|
||||
|
||||
if (msg[10] != 0xff || msg[11] != 0x11 || msg[12] != param[0])
|
||||
{
|
||||
DEBUGOUT_1 ("Setting PPS failed: 0x%02x\n", param[0]);
|
||||
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup parameters to select T=1. */
|
||||
msg[0] = PC_to_RDR_SetParameters;
|
||||
@ -2437,16 +2656,7 @@ ccid_get_atr (ccid_driver_t handle,
|
||||
msg[9] = 0; /* RFU */
|
||||
|
||||
if (!got_param)
|
||||
{
|
||||
/* FIXME: Get those values from the ATR. */
|
||||
msg[10]= 0x01; /* Fi/Di */
|
||||
msg[11]= 0x10; /* LRC, direct convention. */
|
||||
msg[12]= 0; /* Extra guardtime. */
|
||||
msg[13]= 0x41; /* BWI/CWI */
|
||||
msg[14]= 0; /* No clock stoppping. */
|
||||
msg[15]= 254; /* IFSC */
|
||||
msg[16]= 0; /* Does not support non default NAD values. */
|
||||
}
|
||||
memcpy (&msg[10], param, 7);
|
||||
set_msg_len (msg, 7);
|
||||
msglen = 10 + 7;
|
||||
|
||||
@ -2463,6 +2673,12 @@ ccid_get_atr (ccid_driver_t handle,
|
||||
else
|
||||
handle->ifsc = 128; /* Something went wrong, assume 128 bytes. */
|
||||
|
||||
if (handle->nonnull_nad && msglen > 16 && msg[16] == 0)
|
||||
{
|
||||
DEBUGOUT ("Use Null-NAD, clearing handle->nonnull_nad.\n");
|
||||
handle->nonnull_nad = 0;
|
||||
}
|
||||
|
||||
handle->t1_ns = 0;
|
||||
handle->t1_nr = 0;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user