The CCID specification [#!ccid_spec!#] describes a PC_to_RDR_Secure command to perform a PIN verification or PIN modification without sending the PIN to the host. The reader must have a keyboard, and optionnaly a display.
The command format is described in the PCSCv2 part 10 specifications [#!pcsc_v2_part10!#].
The bSendBuffer to pass to SCardControl() contains:
This is the CCID structure used to parameter the PIN verification command.
That is the APDU sent to the card with the PIN code values replaced by the actually entered PIN code. See the CCID specification [#!ccid_spec!#] for a more precise descruption.
#include <winscard.h>
#include <reader.h>
LONG rv;
SCARDHANDLE hCard;
unsigned char bSendBuffer[MAX_BUFFER_SIZE];
unsigned char bRecvBuffer[MAX_BUFFER_SIZE];
DWORD verify_ioctl = 0;
DWORD modify_ioctl = 0;
PIN_VERIFY_STRUCTURE *pin_verify;
/* does the reader support PIN verification? */
rv = SCardControl(hCard, CM_IOCTL_GET_FEATURE_REQUEST, NULL, 0,
bRecvBuffer, sizeof(bRecvBuffer), &length);
/* get the number of elements instead of the complete size */
length /= sizeof(PCSC_TLV_STRUCTURE);
pcsc_tlv = (PCSC_TLV_STRUCTURE *)bRecvBuffer;
for (i = 0; i < length; i++)
{
if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_DIRECT)
verify_ioctl = pcsc_tlv[i].value;
if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_DIRECT)
modify_ioctl = pcsc_tlv[i].value;
}
if (0 == verify_ioctl)
{
printf("Reader %s does not support PIN verification\n",
readers[reader_nb]);
return;
}
pin_verify = (PIN_VERIFY_STRUCTURE *)bSendBuffer;
/* PC/SC v2.0.2 Part 10 PIN verification data structure */
pin_verify -> bTimerOut = 0x00;
pin_verify -> bTimerOut2 = 0x00;
pin_verify -> bmFormatString = 0x82;
pin_verify -> bmPINBlockString = 0x04;
pin_verify -> bmPINLengthFormat = 0x00;
pin_verify -> wPINMaxExtraDigit = HOST_TO_CCID_16(0x0408); /* Min Max */
pin_verify -> bEntryValidationCondition = 0x02; /* validation key pressed */
pin_verify -> bNumberMessage = 0x01;
pin_verify -> wLangId = HOST_TO_CCID_16(0x0904);
pin_verify -> bMsgIndex = 0x00;
pin_verify -> bTeoPrologue[0] = 0x00;
pin_verify -> bTeoPrologue[1] = 0x00;
pin_verify -> bTeoPrologue[2] = 0x00;
/* pin_verify -> ulDataLength = 0x00; we don't know the size yet */
/* APDU: 00 20 00 00 08 30 30 30 30 00 00 00 00 */
offset = 0;
pin_verify -> abData[offset++] = 0x00; /* CLA */
pin_verify -> abData[offset++] = 0x20; /* INS: VERIFY */
pin_verify -> abData[offset++] = 0x00; /* P1 */
pin_verify -> abData[offset++] = 0x00; /* P2 */
pin_verify -> abData[offset++] = 0x08; /* Lc: 8 data bytes */
pin_verify -> abData[offset++] = 0x30; /* '0' */
pin_verify -> abData[offset++] = 0x30; /* '0' */
pin_verify -> abData[offset++] = 0x30; /* '0' */
pin_verify -> abData[offset++] = 0x30; /* '0' */
pin_verify -> abData[offset++] = 0x00; /* '\0' */
pin_verify -> abData[offset++] = 0x00; /* '\0' */
pin_verify -> abData[offset++] = 0x00; /* '\0' */
pin_verify -> abData[offset++] = 0x00; /* '\0' */
pin_verify -> ulDataLength = HOST_TO_CCID_32(offset); /* APDU size */
length = sizeof(PIN_VERIFY_STRUCTURE) + offset -1;
/* -1 because PIN_VERIFY_STRUCTURE contains the first byte of abData[] */
printf("Enter your PIN: ");
fflush(stdout);
rv = SCardControl(hCard, verify_ioctl, bSendBuffer,
length, bRecvBuffer, sizeof(bRecvBuffer), &length);