This adds support for a custom escape command that allows
OmniKey Cardman 3021/3121 (and likely other Omnikey readers)
to handle extended T=1 APDUs.
Note that this patch was written in a way that would minimize
the source changes. It could probably be abstracted away in
a much nicer way.
--- ccid-1.3.11/src/commands.c
+++ ccid-1.3.11/src/commands.c
@@ -81,6 +81,11 @@ enum {
* Makes us map HW_ERROR to "no card inserted".
*/
QUIRK_SLOTSTATUS_HWERR = 1,
+
+ /*
+ * Escape for extended APDUs
+ */
+ QUIRK_ESC_T1_TPDU = 2,
};
struct ccid_quirk {
@@ -102,6 +107,11 @@ static struct ccid_quirk ccid_quirktab[] = {
*/
{ 0x0a5c5800, QUIRK_SLOTSTATUS_HWERR },
{ 0x0a5c5801, QUIRK_SLOTSTATUS_HWERR },
+
+ /*
+ * OmniKey CardMan 3021/3121
+ */
+ { 0x076b3021, QUIRK_ESC_T1_TPDU },
};
static int ccid_quirk_lookup(_ccid_descriptor *ccid, unsigned int *quirks)
@@ -1144,8 +1154,13 @@ RESPONSECODE CmdXfrBlock(unsigned int reader_index,
unsigned int tx_length,
break;
case CCID_CLASS_SHORT_APDU:
- return_value = CmdXfrBlockTPDU_T0(reader_index,
- tx_length, tx_buffer, rx_length, rx_buffer);
+ if (ccid_quirk_required(ccid_descriptor,
QUIRK_ESC_T1_TPDU) && protocol == T_1)
+ return_value = CmdXfrBlockTPDU_T1(reader_index,
tx_length,
+ tx_buffer, rx_length,
rx_buffer);
+ else
+ return_value = CmdXfrBlockTPDU_T0(reader_index,
+ tx_length, tx_buffer, rx_length,
rx_buffer);
+
break;
case CCID_CLASS_EXTENDED_APDU:
@@ -1181,7 +1196,8 @@ RESPONSECODE CmdXfrBlock(unsigned int reader_index,
unsigned int tx_length,
RESPONSECODE CCID_Transmit(unsigned int reader_index, unsigned int tx_length,
const unsigned char tx_buffer[], unsigned short rx_length, unsigned
char bBWI)
{
- unsigned char cmd[10+CMD_BUF_SIZE]; /* CCID + APDU buffer */
+ unsigned char cmd[11+CMD_BUF_SIZE]; /* CCID + APDU buffer */
+ size_t cmdlen;
_ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index);
status_t ret;
@@ -1227,13 +1243,30 @@ RESPONSECODE CCID_Transmit(unsigned int reader_index,
unsigned int tx_length,
}
#endif
- cmd[0] = 0x6F; /* XfrBlock */
- i2dw(tx_length, cmd+1); /* APDU length */
- cmd[5] = ccid_descriptor->bCurrentSlotIndex; /* slot number */
- cmd[6] = (*ccid_descriptor->pbSeq)++;
- cmd[7] = bBWI; /* extend block waiting timeout */
- cmd[8] = rx_length & 0xFF; /* Expected length, in character mode
only */
- cmd[9] = (rx_length >> 8) & 0xFF;
+ if (ccid_quirk_required(ccid_descriptor, QUIRK_ESC_T1_TPDU) &&
+ ccid_descriptor->cardProtocol == SCARD_PROTOCOL_T1)
+ {
+ cmd[0] = 0x6B; /* Escape */
+ i2dw(tx_length+1, cmd+1); /* APDU length
*/
+ cmd[5] = ccid_descriptor->bCurrentSlotIndex; /* slot number
*/
+ cmd[6] = (*ccid_descriptor->pbSeq)++;
+ cmd[7] = cmd[8] = cmd[9] = 0x00; /* RFU */
+ cmd[10] = 0x1A; /* custom cmd
"send TPDU" */
+
+ cmdlen = 11;
+ }
+ else
+ {
+ cmd[0] = 0x6F; /* XfrBlock */
+ i2dw(tx_length, cmd+1); /* APDU length */
+ cmd[5] = ccid_descriptor->bCurrentSlotIndex; /* slot number
*/
+ cmd[6] = (*ccid_descriptor->pbSeq)++;
+ cmd[7] = bBWI; /* extend block waiting timeout */
+ cmd[8] = rx_length & 0xFF; /* Expected length, in
character mode only */
+ cmd[9] = (rx_length >> 8) & 0xFF;
+
+ cmdlen = 10;
+ }
/* check that the command is not too large */
if (tx_length > CMD_BUF_SIZE)
@@ -1242,9 +1275,9 @@ RESPONSECODE CCID_Transmit(unsigned int reader_index,
unsigned int tx_length,
return IFD_NOT_SUPPORTED;
}
- memcpy(cmd+10, tx_buffer, tx_length);
+ memcpy(cmd+cmdlen, tx_buffer, tx_length);
- ret = WritePort(reader_index, 10+tx_length, cmd);
+ ret = WritePort(reader_index, cmdlen+tx_length, cmd);
if (STATUS_NO_SUCH_DEVICE == ret)
return IFD_NO_SUCH_DEVICE;
if (ret != STATUS_SUCCESS)
@@ -1262,7 +1295,7 @@ RESPONSECODE CCID_Transmit(unsigned int reader_index,
unsigned int tx_length,
RESPONSECODE CCID_Receive(unsigned int reader_index, unsigned int *rx_length,
unsigned char rx_buffer[], unsigned char *chain_parameter)
{
- unsigned char cmd[10+CMD_BUF_SIZE]; /* CCID + APDU buffer */
+ unsigned char cmd[11+CMD_BUF_SIZE]; /* CCID + APDU buffer */
unsigned int length;
RESPONSECODE return_value = IFD_SUCCESS;
status_t ret;
@@ -1438,8 +1471,13 @@ time_request:
}
length = dw2i(cmd, 1);
- if (length <= *rx_length)
+ if (length <= *rx_length) {
*rx_length = length;
+
+ if (ccid_quirk_required(ccid_descriptor, QUIRK_ESC_T1_TPDU) &&
+ ccid_descriptor->cardProtocol == SCARD_PROTOCOL_T1)
+ *rx_length--;
+ }
else
{
DEBUG_CRITICAL2("overrun by %d bytes", length - *rx_length);
@@ -1454,7 +1492,13 @@ time_request:
return_value = IFD_COMMUNICATION_ERROR;
}
else
- memcpy(rx_buffer, cmd+10, length);
+ {
+ if (ccid_quirk_required(ccid_descriptor, QUIRK_ESC_T1_TPDU) &&
+ ccid_descriptor->cardProtocol == SCARD_PROTOCOL_T1)
+ memcpy(rx_buffer, cmd+11, length-1);
+ else
+ memcpy(rx_buffer, cmd+10, length);
+ }
/* Extended case?
* Only valid for RDR_to_PC_DataBlock frames */
@@ -1636,6 +1680,12 @@ static RESPONSECODE CmdXfrBlockTPDU_T0(unsigned int
reader_index,
}
else
#endif
+ if (ccid_quirk_required(ccid_descriptor, QUIRK_ESC_T1_TPDU))
+ {
+ DEBUG_CRITICAL3("Ignoring dwMaxCCDMessageLength (tx=%d,
max=%d)",
+ tx_length,
ccid_descriptor->dwMaxCCIDMessageLength-10);
+ }
+ else
{
DEBUG_CRITICAL3("Command too long (%d bytes) for max:
%d bytes",
tx_length,
ccid_descriptor->dwMaxCCIDMessageLength-10);
_______________________________________________
Muscle mailing list
[email protected]
http://lists.drizzle.com/mailman/listinfo/muscle