You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1819 lines
54 KiB
1819 lines
54 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1996 - 1999
|
|
|
|
Module Name:
|
|
|
|
smcprot.c
|
|
|
|
Author:
|
|
|
|
Klaus U. Schutz
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Revision History:
|
|
|
|
- Dec. 96: Initial version
|
|
- Nov. 97: Release 1.0
|
|
- Feb. 98: T=1 uses now the min of IFSC, IFSD for SmartcardT1Request
|
|
T=1 fixed number of bytes to invert for inverse convention cards
|
|
|
|
|
|
--*/
|
|
|
|
#ifdef SMCLIB_VXD
|
|
|
|
#define try
|
|
#define leave goto __label
|
|
#define finally __label:
|
|
|
|
#else
|
|
|
|
#ifndef SMCLIB_CE
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ntddk.h>
|
|
#endif // SMCLIB_CE
|
|
#endif
|
|
|
|
#include "smclib.h"
|
|
#define SMARTCARD_POOL_TAG 'bLCS'
|
|
|
|
#ifdef DEBUG_INTERFACE
|
|
BOOLEAN
|
|
DebugT1Reply(
|
|
PSMARTCARD_EXTENSION SmartcardExtension
|
|
);
|
|
|
|
void
|
|
DebugGetT1Request(
|
|
PSMARTCARD_EXTENSION SmartcardExtension,
|
|
NTSTATUS Status
|
|
);
|
|
|
|
BOOLEAN
|
|
DebugSetT1Request(
|
|
PSMARTCARD_EXTENSION SmartcardExtension
|
|
);
|
|
#endif // DEBUG_INTERFACE
|
|
|
|
void
|
|
DumpData(
|
|
const ULONG DebugLevel,
|
|
PUCHAR Data,
|
|
ULONG DataLen
|
|
);
|
|
|
|
//
|
|
// Usually an io-request consists only of the SCARD_IO_REQUEST header
|
|
// followed by the data to be transmitted. To allow modification of
|
|
// protocol data, it is possible to pass down the data to be modified.
|
|
// These data are ASN1 encoded.
|
|
//
|
|
typedef struct _IO_HEADER {
|
|
SCARD_IO_REQUEST ScardIoRequest;
|
|
UCHAR Asn1Data[1];
|
|
} IO_HEADER, *PIO_HEADER;
|
|
|
|
NTSTATUS
|
|
#ifdef SMCLIB_VXD
|
|
SMCLIB_SmartcardRawRequest(
|
|
#else
|
|
SmartcardRawRequest(
|
|
#endif
|
|
PSMARTCARD_EXTENSION SmartcardExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
-
|
|
|
|
--*/
|
|
{
|
|
PSMARTCARD_REQUEST smartcardRequest = &(SmartcardExtension->SmartcardRequest);
|
|
|
|
if ((smartcardRequest->BufferSize < smartcardRequest->BufferLength) ||
|
|
(smartcardRequest->BufferSize - smartcardRequest->BufferLength
|
|
<= SmartcardExtension->IoRequest.RequestBufferLength)) {
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// Copy request data to the buffer
|
|
//
|
|
RtlCopyMemory(
|
|
&smartcardRequest->Buffer[smartcardRequest->BufferLength],
|
|
SmartcardExtension->IoRequest.RequestBuffer,
|
|
SmartcardExtension->IoRequest.RequestBufferLength
|
|
);
|
|
|
|
//
|
|
// If the card uses invers convention invert the data
|
|
//
|
|
if (SmartcardExtension->CardCapabilities.InversConvention) {
|
|
|
|
SmartcardInvertData(
|
|
&smartcardRequest->Buffer[smartcardRequest->BufferLength],
|
|
SmartcardExtension->IoRequest.RequestBufferLength
|
|
);
|
|
}
|
|
|
|
//
|
|
// number of bytes to send to the reader
|
|
//
|
|
smartcardRequest->BufferLength +=
|
|
SmartcardExtension->IoRequest.RequestBufferLength;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
#ifdef SMCLIB_VXD
|
|
SMCLIB_SmartcardRawReply(
|
|
#else
|
|
SmartcardRawReply(
|
|
#endif
|
|
PSMARTCARD_EXTENSION SmartcardExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
-
|
|
|
|
--*/
|
|
{
|
|
if (SmartcardExtension->IoRequest.ReplyBufferLength <
|
|
SmartcardExtension->SmartcardReply.BufferLength) {
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// Copy data to user buffer
|
|
//
|
|
RtlCopyMemory(
|
|
SmartcardExtension->IoRequest.ReplyBuffer,
|
|
SmartcardExtension->SmartcardReply.Buffer,
|
|
SmartcardExtension->SmartcardReply.BufferLength
|
|
);
|
|
|
|
//
|
|
// Length of data to return
|
|
//
|
|
*SmartcardExtension->IoRequest.Information =
|
|
SmartcardExtension->SmartcardReply.BufferLength;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
#ifdef SMCLIB_VXD
|
|
SMCLIB_SmartcardT0Request(
|
|
#else
|
|
SmartcardT0Request(
|
|
#endif
|
|
PSMARTCARD_EXTENSION SmartcardExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prepares the buffer SmartcardExtension->SmartcardRequest.Buffer
|
|
to send data to the smart card
|
|
|
|
Arguments:
|
|
|
|
NOTE: On input SmartcardExtension->SmartcardRequest.BufferLenght indicates
|
|
the offset where we should copy the data to. This is usually
|
|
used by readers that needs to have some bytes as header bytes
|
|
to send to the reader before the data bytes for the card
|
|
|
|
Return Value:
|
|
|
|
-
|
|
|
|
--*/
|
|
{
|
|
PSMARTCARD_REQUEST smartcardRequest = &SmartcardExtension->SmartcardRequest;
|
|
PSCARD_IO_REQUEST scardIoRequest;
|
|
PUCHAR ioRequestData;
|
|
ULONG ioRequestDataLength, headerSize;
|
|
|
|
if ((smartcardRequest->BufferSize < smartcardRequest->BufferLength) ||
|
|
(smartcardRequest->BufferSize - smartcardRequest->BufferLength
|
|
<= SmartcardExtension->IoRequest.RequestBufferLength)) {
|
|
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
(TEXT("%s!SmartcardT0Request: IoRequest.RequestBuffer too big\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
scardIoRequest = (PSCARD_IO_REQUEST)
|
|
SmartcardExtension->IoRequest.RequestBuffer;
|
|
|
|
ioRequestData =
|
|
SmartcardExtension->IoRequest.RequestBuffer +
|
|
sizeof(SCARD_IO_REQUEST);
|
|
|
|
ioRequestDataLength =
|
|
SmartcardExtension->IoRequest.RequestBufferLength -
|
|
sizeof(SCARD_IO_REQUEST);
|
|
|
|
//
|
|
// Copy T=0 protocol-info into buffer
|
|
//
|
|
RtlCopyMemory(
|
|
&smartcardRequest->Buffer[smartcardRequest->BufferLength],
|
|
ioRequestData,
|
|
ioRequestDataLength
|
|
);
|
|
|
|
//
|
|
// Remember number of bytes for the header offset
|
|
//
|
|
headerSize =
|
|
smartcardRequest->BufferLength;
|
|
|
|
//
|
|
// Number of bytes to send to the reader
|
|
//
|
|
smartcardRequest->BufferLength +=
|
|
ioRequestDataLength;
|
|
|
|
if (ioRequestDataLength < 4) {
|
|
|
|
//
|
|
// A T=0 request needs at least 4 bytes
|
|
//
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
(TEXT("%s!SmartcardT0Request: TPDU is too short (%d). Must be at least 4 bytes\n"),
|
|
DRIVER_NAME,
|
|
ioRequestDataLength)
|
|
);
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
PUCHAR requestBuffer = SmartcardExtension->SmartcardRequest.Buffer;
|
|
|
|
if (ioRequestDataLength <= 5) {
|
|
|
|
//
|
|
// We request to read data from the card
|
|
//
|
|
SmartcardExtension->T0.Lc = 0;
|
|
|
|
if (ioRequestDataLength == 4) {
|
|
|
|
//
|
|
// This is a special case where a 4 byte APDU is mapped to
|
|
// a 5 byte TPDU (ISO 7816 - Part 4, Annex A, A.1 Case 1)
|
|
// This case requires that we append a 0 to the
|
|
// APDU to make it a TPDU
|
|
//
|
|
SmartcardExtension->T0.Le = 0;
|
|
smartcardRequest->Buffer[headerSize + 4] = 0;
|
|
smartcardRequest->BufferLength += 1;
|
|
|
|
} else {
|
|
|
|
SmartcardExtension->T0.Le =
|
|
(requestBuffer[headerSize + 4] ? requestBuffer[headerSize + 4] : 256);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// We want to send data to the card
|
|
//
|
|
SmartcardExtension->T0.Lc = requestBuffer[headerSize + 4];
|
|
SmartcardExtension->T0.Le = 0;
|
|
|
|
if (SmartcardExtension->T0.Lc != ioRequestDataLength - 5) {
|
|
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
(TEXT("%s!SmartcardT0Request: Lc(%d) in TPDU doesn't match number of bytes to send(%d).\n"),
|
|
DRIVER_NAME,
|
|
SmartcardExtension->T0.Lc,
|
|
ioRequestDataLength - 5)
|
|
);
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
#if DEBUG
|
|
{
|
|
PUCHAR T0Data = requestBuffer + headerSize;
|
|
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT(" CLA: %02X\n INS: %02X\n P1: %02X\n P2: %02X\n Lc: %02X\n Le: %02X\n"),
|
|
T0Data[0], T0Data[1], T0Data[2], T0Data[3],
|
|
SmartcardExtension->T0.Lc,
|
|
SmartcardExtension->T0.Le)
|
|
);
|
|
|
|
if (SmartcardExtension->T0.Lc) {
|
|
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT(" Data: "))
|
|
);
|
|
|
|
DumpData(
|
|
DEBUG_PROTOCOL,
|
|
T0Data + 5,
|
|
SmartcardExtension->T0.Lc
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// If the card uses invers convention invert the data
|
|
//
|
|
if (SmartcardExtension->CardCapabilities.InversConvention) {
|
|
|
|
SmartcardInvertData(
|
|
&smartcardRequest->Buffer[headerSize],
|
|
smartcardRequest->BufferLength - headerSize
|
|
);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
#ifdef APDU_SUPPORT
|
|
ULONG requestLength = SmartcardExtension->SmartcardRequest.BufferLength;
|
|
ULONG L;
|
|
|
|
//
|
|
// Figure out Lc and Le
|
|
// (See 'Decoding of the command APDUs' in ISO Part 4, 5.3.2)
|
|
// (Variable names used are according to ISO designations)
|
|
//
|
|
L = requestLength - 4;
|
|
|
|
if (L > 65536) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (L == 0) {
|
|
|
|
//
|
|
// Lc = 0, No Data, Le = 0;
|
|
//
|
|
SmartcardExtension->T0.Lc = 0;
|
|
SmartcardExtension->T0.Le = 0;
|
|
|
|
} else if (L == 1) {
|
|
|
|
//
|
|
// Case 2S, Lc = 0, Le = B1
|
|
//
|
|
SmartcardExtension->T0.Lc = 0;
|
|
SmartcardExtension->T0.Le = requestBuffer[4];
|
|
|
|
} else {
|
|
|
|
UCHAR B1 = requestBuffer[4];
|
|
|
|
if (B1 != 0) {
|
|
|
|
//
|
|
// Short form
|
|
//
|
|
if (L == (ULONG) (1 + B1)) {
|
|
|
|
//
|
|
// Case 3S, Lc = B1, Le = 0
|
|
//
|
|
SmartcardExtension->T0.Lc = B1;
|
|
SmartcardExtension->T0.Le = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Case 4S, Lc = B1, Le = BL
|
|
//
|
|
SmartcardExtension->T0.Lc = B1;
|
|
SmartcardExtension->T0.Le = requestBuffer[L - 1];
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Extended form
|
|
//
|
|
if (L == 3) {
|
|
|
|
//
|
|
// Case 2E, Lc = 0, Le = B(L - 1, L)
|
|
//
|
|
LENGTH length;
|
|
|
|
length.l.l0 = 0;
|
|
length.b.b0 = requestBuffer[L - 1];
|
|
length.b.b1 = requestBuffer[L - 2];
|
|
|
|
SmartcardExtension->T0.Lc = 0;
|
|
SmartcardExtension->T0.Le = (length.l.l0 ? length.l.l0 : 65536);
|
|
|
|
} else {
|
|
|
|
LENGTH length;
|
|
|
|
length.l.l0 = 0;
|
|
length.b.b0 = requestBuffer[6];
|
|
length.b.b1 = requestBuffer[5];
|
|
|
|
SmartcardExtension->T0.Lc = length.l.l0;
|
|
|
|
if (L == 3 + length.l.l0) {
|
|
|
|
//
|
|
// Case 3E, Lc = B(2,3)
|
|
//
|
|
SmartcardExtension->T0.Le = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Case 4E, Lc = B(2,3), Le = B(L - 1, L)
|
|
//
|
|
LENGTH length;
|
|
|
|
length.l.l0 = 0;
|
|
length.b.b0 = requestBuffer[L - 1];
|
|
length.b.b1 = requestBuffer[L - 2];
|
|
|
|
SmartcardExtension->T0.Le = (length.l.l0 ? length.l.l0 : 65536);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
#ifdef SMCLIB_VXD
|
|
SMCLIB_SmartcardT0Reply(
|
|
#else
|
|
SmartcardT0Reply(
|
|
#endif
|
|
PSMARTCARD_EXTENSION SmartcardExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
-
|
|
|
|
--*/
|
|
{
|
|
PSMARTCARD_REPLY smartcardReply = &SmartcardExtension->SmartcardReply;
|
|
|
|
//
|
|
// The reply must be at least to 2 bytes long. These 2 bytes are
|
|
// the return value (StatusWord) from the smart card
|
|
//
|
|
if (smartcardReply->BufferLength < 2) {
|
|
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
|
|
if (SmartcardExtension->IoRequest.ReplyBufferLength <
|
|
smartcardReply->BufferLength + sizeof(SCARD_IO_REQUEST)) {
|
|
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
(TEXT("%s!SmartcardT0Request: ReplyBuffer too small\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
// Copy protocol header to user buffer
|
|
RtlCopyMemory(
|
|
SmartcardExtension->IoRequest.ReplyBuffer,
|
|
SmartcardExtension->IoRequest.RequestBuffer,
|
|
sizeof(SCARD_IO_REQUEST)
|
|
);
|
|
|
|
// If the card uses invers convention invert the data
|
|
if (SmartcardExtension->CardCapabilities.InversConvention) {
|
|
|
|
SmartcardInvertData(
|
|
smartcardReply->Buffer,
|
|
smartcardReply->BufferLength
|
|
);
|
|
}
|
|
|
|
// Copy all data to user buffer
|
|
RtlCopyMemory(
|
|
SmartcardExtension->IoRequest.ReplyBuffer + sizeof(SCARD_IO_REQUEST),
|
|
smartcardReply->Buffer,
|
|
smartcardReply->BufferLength
|
|
);
|
|
|
|
// Length of answer
|
|
*SmartcardExtension->IoRequest.Information =
|
|
smartcardReply->BufferLength +
|
|
sizeof(SCARD_IO_REQUEST);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
BOOLEAN
|
|
SmartcardT1Chksum(
|
|
PUCHAR Block,
|
|
UCHAR Edc,
|
|
BOOLEAN Verify
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calculates the epilogue field for a T1 block. It calculates the LRC
|
|
for all the data in the IBlock.
|
|
|
|
Arguments:
|
|
|
|
Block - T1 Information block, to be sent, or just read, from the card.
|
|
Edc - ErrorDetectionCode as described in ISO
|
|
Verify - If this is a block that was recieved form the card, TRUE will cause this routine
|
|
to check the epilogue field, included with this buffer, against the calculated one
|
|
|
|
Return Value:
|
|
|
|
TRUE if Verify = TRUE and epilogue fields match or Verify = FALSE
|
|
FALSE if Verify = TRUE and an error was detected (mismatch)
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
USHORT i;
|
|
UCHAR lrc;
|
|
USHORT crc = 0;
|
|
USHORT offset = Block[2] + SCARD_T1_PROLOGUE_LENGTH;
|
|
|
|
unsigned short crc16a[] = {
|
|
0000000, 0140301, 0140601, 0000500,
|
|
0141401, 0001700, 0001200, 0141101,
|
|
0143001, 0003300, 0003600, 0143501,
|
|
0002400, 0142701, 0142201, 0002100,
|
|
};
|
|
unsigned short crc16b[] = {
|
|
0000000, 0146001, 0154001, 0012000,
|
|
0170001, 0036000, 0024000, 0162001,
|
|
0120001, 0066000, 0074000, 0132001,
|
|
0050000, 0116001, 0104001, 0043000,
|
|
};
|
|
|
|
if (Edc & T1_CRC_CHECK) {
|
|
|
|
UCHAR tmp;
|
|
|
|
// Calculate CRC using tables.
|
|
for ( i = 0; i < offset; i++) {
|
|
|
|
tmp = Block[i] ^ (UCHAR) crc;
|
|
crc = (crc >> 8) ^ crc16a[tmp & 0x0f] ^ crc16b[tmp >> 4];
|
|
}
|
|
|
|
if (Verify) {
|
|
|
|
if (crc == (Block[offset + 1] | (Block[offset] << 8))) {
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
} else {
|
|
|
|
Block[offset] = (UCHAR) (crc >> 8 ); //MSB of crc
|
|
Block[offset + 1] = (UCHAR) (crc & 0x00ff); //LSB of crc
|
|
return TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
// Calculate LRC by X-Oring all the bytes.
|
|
lrc = Block[0];
|
|
|
|
for(i = 1; i < offset; i++){
|
|
|
|
lrc ^= Block[i];
|
|
}
|
|
|
|
if (Verify) {
|
|
|
|
return (lrc == Block[offset] ? TRUE : FALSE);
|
|
|
|
} else {
|
|
|
|
Block[offset] = lrc;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#if (DEBUG)
|
|
static
|
|
void
|
|
DumpT1Block(
|
|
PUCHAR Buffer,
|
|
UCHAR Edc
|
|
)
|
|
{
|
|
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT(" NAD: %02X\n PCB: %02X\n LEN: %02X\n INF: "),
|
|
Buffer[0], Buffer[1], Buffer[2])
|
|
);
|
|
|
|
if (Buffer[2] == 0) {
|
|
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("- "))
|
|
);
|
|
}
|
|
|
|
DumpData(
|
|
DEBUG_PROTOCOL,
|
|
Buffer + 3,
|
|
Buffer[2]
|
|
);
|
|
|
|
if (Edc & T1_CRC_CHECK) {
|
|
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("\n CRC: %02X %02X"),
|
|
Buffer[Buffer[2] + 3],
|
|
Buffer[Buffer[2] + 4])
|
|
);
|
|
|
|
} else {
|
|
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("\n LRC: %02X"),
|
|
Buffer[Buffer[2] + 3])
|
|
);
|
|
}
|
|
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("\n"))
|
|
);
|
|
}
|
|
#endif
|
|
|
|
#if DEBUG
|
|
#pragma optimize( "", off )
|
|
#endif
|
|
|
|
NTSTATUS
|
|
#ifdef SMCLIB_VXD
|
|
SMCLIB_SmartcardT1Request(
|
|
#else
|
|
SmartcardT1Request(
|
|
#endif
|
|
PSMARTCARD_EXTENSION SmartcardExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
SmartcardExtension - Supplies a pointer to the smart card data
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PSMARTCARD_REQUEST smartcardRequest = &(SmartcardExtension->SmartcardRequest);
|
|
PIO_HEADER IoHeader = (PIO_HEADER) SmartcardExtension->IoRequest.RequestBuffer;
|
|
T1_BLOCK_FRAME t1SendFrame;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
#if (DEBUG)
|
|
ULONG headerSize = smartcardRequest->BufferLength;
|
|
#endif
|
|
|
|
#ifdef DEBUG_INTERFACE
|
|
if (DebugSetT1Request(SmartcardExtension)) {
|
|
|
|
//
|
|
// the debugger gave us a new packet that we have to
|
|
// send instead of the original packet which will be sent later
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
if (SmartcardExtension->T1.WaitForReply) {
|
|
|
|
// we did not get an answer to our last request
|
|
SmartcardExtension->T1.State = T1_INIT;
|
|
}
|
|
SmartcardExtension->T1.WaitForReply = TRUE;
|
|
|
|
__try {
|
|
|
|
switch (SmartcardExtension->T1.State) {
|
|
|
|
case T1_INIT:
|
|
SmartcardExtension->T1.State = T1_IFS_REQUEST;
|
|
|
|
// NO break here !!!
|
|
|
|
case T1_START:
|
|
//
|
|
// Since this is the very first block in a
|
|
// transmission we reset the resynch counter
|
|
//
|
|
SmartcardExtension->T1.Resynch = 0;
|
|
|
|
//
|
|
// Allocate a buffer that receives the result.
|
|
// This is necessary since we otherwise overwite our
|
|
// request data which we might wish to resend in case
|
|
// of an error
|
|
//
|
|
if (SmartcardExtension->T1.ReplyData != NULL) {
|
|
|
|
#ifdef SMCLIB_VXD
|
|
_HeapFree(SmartcardExtension->T1.ReplyData, 0);
|
|
#elif defined(SMCLIB_CE)
|
|
LocalFree(SmartcardExtension->T1.ReplyData);
|
|
#else
|
|
ExFreePool(SmartcardExtension->T1.ReplyData);
|
|
#endif
|
|
SmartcardExtension->T1.ReplyData = NULL;
|
|
}
|
|
|
|
if (SmartcardExtension->IoRequest.ReplyBufferLength <
|
|
IoHeader->ScardIoRequest.cbPciLength + 2) {
|
|
|
|
//
|
|
// We should at least be able to store
|
|
// the io-header plus SW1 and SW2
|
|
//
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
__leave;
|
|
}
|
|
#ifdef SMCLIB_VXD
|
|
SmartcardExtension->T1.ReplyData = (PUCHAR) _HeapAllocate(
|
|
SmartcardExtension->IoRequest.ReplyBufferLength,
|
|
0
|
|
);
|
|
#elif defined(SMCLIB_CE)
|
|
SmartcardExtension->T1.ReplyData = (PUCHAR) LocalAlloc(LPTR,
|
|
SmartcardExtension->IoRequest.ReplyBufferLength
|
|
);
|
|
|
|
#else
|
|
SmartcardExtension->T1.ReplyData = ExAllocatePool(
|
|
NonPagedPool,
|
|
SmartcardExtension->IoRequest.ReplyBufferLength
|
|
);
|
|
#endif
|
|
ASSERT(SmartcardExtension->T1.ReplyData != NULL);
|
|
|
|
if (SmartcardExtension->T1.ReplyData == NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
__leave;
|
|
}
|
|
|
|
// No break here !!!
|
|
|
|
case T1_RESTART:
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Request: T1_%s\n"),
|
|
DRIVER_NAME,
|
|
(SmartcardExtension->T1.State == T1_START ? TEXT("START") : TEXT("RESTART")))
|
|
);
|
|
|
|
// Copy protocol header back to user buffer
|
|
RtlCopyMemory(
|
|
SmartcardExtension->T1.ReplyData,
|
|
SmartcardExtension->IoRequest.RequestBuffer,
|
|
IoHeader->ScardIoRequest.cbPciLength
|
|
);
|
|
|
|
//
|
|
// Check for the special case where the io-header is followed
|
|
// by asn1 data that contains the NAD value to be used.
|
|
// This was done for VISA, because they need access to the NAD.
|
|
// The NAD is ASN1 encoded as 81h 00h NAD 00h
|
|
//
|
|
if (IoHeader->ScardIoRequest.cbPciLength > sizeof(SCARD_IO_REQUEST) &&
|
|
IoHeader->Asn1Data[0] == 0x81 &&
|
|
IoHeader->Asn1Data[1] == 0x01 &&
|
|
IoHeader->Asn1Data[3] == 0x00) {
|
|
|
|
SmartcardExtension->T1.NAD = IoHeader->Asn1Data[2];
|
|
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Request: NAD set to %02xh\n"),
|
|
DRIVER_NAME,
|
|
SmartcardExtension->T1.NAD)
|
|
);
|
|
}
|
|
|
|
// Initialize the T1 protocol data
|
|
SmartcardExtension->T1.BytesToSend =
|
|
SmartcardExtension->IoRequest.RequestBufferLength -
|
|
IoHeader->ScardIoRequest.cbPciLength;
|
|
|
|
SmartcardExtension->T1.BytesSent = 0;
|
|
SmartcardExtension->T1.BytesReceived = 0;
|
|
//
|
|
// This is the maximum number of bytes that the smartcard can
|
|
// accept in a single block. The smartcard can extend this size
|
|
// during the transmission
|
|
//
|
|
SmartcardExtension->T1.IFSC =
|
|
SmartcardExtension->CardCapabilities.T1.IFSC;
|
|
|
|
//
|
|
// Since this is the first block in a transmission we reset
|
|
// the re-transmission counter.
|
|
//
|
|
SmartcardExtension->T1.Resend = 0;
|
|
SmartcardExtension->T1.OriginalState = 0;
|
|
|
|
SmartcardExtension->T1.MoreData = FALSE;
|
|
//
|
|
// NO break here !!!
|
|
//
|
|
// After a card reset we first send an IFS-Request to the card.
|
|
// Otherwise we start with an I-Block
|
|
//
|
|
|
|
case T1_IFS_REQUEST:
|
|
if (SmartcardExtension->T1.State == T1_IFS_REQUEST) {
|
|
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Request: T1_IFSD_REQUEST\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
SmartcardExtension->T1.State =
|
|
T1_IFS_REQUEST;
|
|
|
|
t1SendFrame.Nad = SmartcardExtension->T1.NAD;
|
|
//
|
|
// IFS request.
|
|
// Send our IFSD size to the card
|
|
//
|
|
t1SendFrame.Pcb = 0xC1;
|
|
t1SendFrame.Len = 1;
|
|
t1SendFrame.Inf = &SmartcardExtension->T1.IFSD;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
SmartcardExtension->T1.State = T1_I_BLOCK;
|
|
}
|
|
|
|
// No break here !!
|
|
|
|
case T1_I_BLOCK:
|
|
SmartcardExtension->T1.State = T1_I_BLOCK;
|
|
|
|
//
|
|
// Set the number of bytes we will transmit to the card.
|
|
// This is the lesser of IFSD and IFSC
|
|
//
|
|
SmartcardExtension->T1.InfBytesSent = SmartcardExtension->T1.IFSC;
|
|
|
|
if (SmartcardExtension->T1.InfBytesSent > SmartcardExtension->T1.IFSD) {
|
|
|
|
SmartcardExtension->T1.InfBytesSent = SmartcardExtension->T1.IFSD;
|
|
}
|
|
|
|
// Send either max frame size or remaining bytes
|
|
if (SmartcardExtension->T1.BytesToSend > SmartcardExtension->T1.InfBytesSent) {
|
|
|
|
SmartcardExtension->T1.MoreData = TRUE;
|
|
t1SendFrame.Len = SmartcardExtension->T1.InfBytesSent;
|
|
|
|
} else {
|
|
|
|
SmartcardExtension->T1.MoreData = FALSE;
|
|
t1SendFrame.Len = (UCHAR) SmartcardExtension->T1.BytesToSend;
|
|
}
|
|
|
|
t1SendFrame.Nad = SmartcardExtension->T1.NAD;
|
|
|
|
//
|
|
// ProtocolControlByte:
|
|
// b7 - SendSequenceNumber
|
|
// b6 - MoreDatatBit
|
|
//
|
|
t1SendFrame.Pcb =
|
|
(SmartcardExtension->T1.SSN) << 6 |
|
|
(SmartcardExtension->T1.MoreData ? T1_MORE_DATA : 0);
|
|
|
|
t1SendFrame.Inf =
|
|
SmartcardExtension->IoRequest.RequestBuffer +
|
|
IoHeader->ScardIoRequest.cbPciLength +
|
|
SmartcardExtension->T1.BytesSent;
|
|
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Request: I(%d.%d) ->\n"),
|
|
DRIVER_NAME,
|
|
SmartcardExtension->T1.SSN,
|
|
(SmartcardExtension->T1.MoreData ? 1 : 0))
|
|
);
|
|
break;
|
|
|
|
case T1_R_BLOCK:
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Request: R(%d) ->\n"),
|
|
DRIVER_NAME,
|
|
SmartcardExtension->T1.RSN)
|
|
);
|
|
|
|
t1SendFrame.Nad = SmartcardExtension->T1.NAD;
|
|
//
|
|
// ProtocolControlByte:
|
|
// b5 - SequenceNumber
|
|
// b1-4 - ErrorCode
|
|
//
|
|
t1SendFrame.Pcb =
|
|
0x80 |
|
|
(SmartcardExtension->T1.RSN) << 4 |
|
|
(SmartcardExtension->T1.LastError);
|
|
|
|
//
|
|
// If this R-Block is a response to an error
|
|
// we have to restore to the original state we had before
|
|
//
|
|
if (SmartcardExtension->T1.LastError) {
|
|
|
|
SmartcardExtension->T1.LastError = 0;
|
|
|
|
//
|
|
// We must have a defined original state here
|
|
//
|
|
ASSERT(SmartcardExtension->T1.OriginalState != 0);
|
|
|
|
if (SmartcardExtension->T1.OriginalState == 0) {
|
|
|
|
SmartcardExtension->T1.State = T1_START;
|
|
status = STATUS_INTERNAL_ERROR;
|
|
__leave;
|
|
}
|
|
|
|
SmartcardExtension->T1.State =
|
|
SmartcardExtension->T1.OriginalState;
|
|
}
|
|
|
|
t1SendFrame.Len = 0;
|
|
t1SendFrame.Inf = NULL;
|
|
break;
|
|
|
|
case T1_IFS_RESPONSE:
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Request: T1_IFSD_RESPONSE\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
// Restore to the original state we had before
|
|
ASSERT(SmartcardExtension->T1.OriginalState != 0);
|
|
|
|
SmartcardExtension->T1.State =
|
|
SmartcardExtension->T1.OriginalState;
|
|
|
|
t1SendFrame.Nad = SmartcardExtension->T1.NAD;
|
|
|
|
// Send IFS response
|
|
t1SendFrame.Pcb = 0xE1;
|
|
t1SendFrame.Len = 1;
|
|
|
|
// New length of INF-Field
|
|
t1SendFrame.Inf = &SmartcardExtension->T1.IFSC;
|
|
break;
|
|
|
|
case T1_RESYNCH_REQUEST:
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Request: T1_RESYNCH_REQUEST\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
t1SendFrame.Nad = SmartcardExtension->T1.NAD;
|
|
|
|
// Resynch request
|
|
t1SendFrame.Pcb = 0xC0;
|
|
t1SendFrame.Len = 0;
|
|
t1SendFrame.Inf = NULL;
|
|
|
|
// Set the send sequence number to 0
|
|
SmartcardExtension->T1.SSN = 0;
|
|
break;
|
|
|
|
case T1_ABORT_REQUEST:
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Request: T1_ABORT_REQUEST\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
t1SendFrame.Nad = SmartcardExtension->T1.NAD;
|
|
|
|
// Send ABORT request
|
|
t1SendFrame.Pcb = 0xC2;
|
|
t1SendFrame.Len = 0;
|
|
t1SendFrame.Inf = NULL;
|
|
break;
|
|
|
|
case T1_ABORT_RESPONSE:
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Request: T1_ABORT_RESPONSE\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
SmartcardExtension->T1.State = T1_START;
|
|
|
|
t1SendFrame.Nad = SmartcardExtension->T1.NAD;
|
|
|
|
// Send ABORT response
|
|
t1SendFrame.Pcb = 0xE2;
|
|
t1SendFrame.Len = 0;
|
|
t1SendFrame.Inf = NULL;
|
|
break;
|
|
|
|
case T1_WTX_RESPONSE:
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Request: T1_WTX_RESPONSE\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
// Restore to the original state we had before
|
|
ASSERT(SmartcardExtension->T1.OriginalState != 0);
|
|
|
|
SmartcardExtension->T1.State =
|
|
SmartcardExtension->T1.OriginalState;
|
|
|
|
SmartcardExtension->T1.OriginalState = 0;
|
|
|
|
t1SendFrame.Nad = SmartcardExtension->T1.NAD;
|
|
|
|
// Send WTX response
|
|
t1SendFrame.Pcb = 0xE3;
|
|
t1SendFrame.Len = 1;
|
|
t1SendFrame.Inf = &SmartcardExtension->T1.Wtx;
|
|
break;
|
|
|
|
}
|
|
|
|
// Insert Node Address byte
|
|
smartcardRequest->Buffer[smartcardRequest->BufferLength] =
|
|
t1SendFrame.Nad;
|
|
|
|
// Insert ProtocolControlByte
|
|
smartcardRequest->Buffer[smartcardRequest->BufferLength + 1] =
|
|
t1SendFrame.Pcb;
|
|
|
|
// Length of INF field
|
|
smartcardRequest->Buffer[smartcardRequest->BufferLength + 2] =
|
|
t1SendFrame.Len;
|
|
|
|
// Insert INF field data
|
|
if (t1SendFrame.Len > 0) {
|
|
|
|
RtlCopyMemory(
|
|
&smartcardRequest->Buffer[smartcardRequest->BufferLength + 3],
|
|
t1SendFrame.Inf,
|
|
t1SendFrame.Len
|
|
);
|
|
}
|
|
|
|
// Compute checksum
|
|
SmartcardT1Chksum(
|
|
&smartcardRequest->Buffer[smartcardRequest->BufferLength],
|
|
SmartcardExtension->CardCapabilities.T1.EDC,
|
|
FALSE
|
|
);
|
|
|
|
#if defined(DEBUG)
|
|
#if defined(SMCLIB_NT)
|
|
if (SmartcardGetDebugLevel() & DEBUG_T1_TEST) {
|
|
|
|
LARGE_INTEGER Ticks;
|
|
UCHAR RandomVal;
|
|
KeQueryTickCount(&Ticks);
|
|
|
|
RandomVal = (UCHAR) Ticks.LowPart % 4;
|
|
|
|
if (RandomVal == 0) {
|
|
|
|
smartcardRequest->Buffer[smartcardRequest->BufferLength - 1] += 1;
|
|
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
(TEXT("%s!SmartcardT1Request: Simulating bad checksum\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
DumpT1Block(
|
|
smartcardRequest->Buffer + headerSize,
|
|
SmartcardExtension->CardCapabilities.T1.EDC
|
|
);
|
|
#endif
|
|
|
|
//
|
|
// If the card uses invers convention invert the data
|
|
// NOTE: do not invert any header data the reader may use
|
|
//
|
|
if (SmartcardExtension->CardCapabilities.InversConvention) {
|
|
|
|
SmartcardInvertData(
|
|
&smartcardRequest->Buffer[smartcardRequest->BufferLength],
|
|
(SmartcardExtension->CardCapabilities.T1.EDC & T1_CRC_CHECK ? 5 : 4) +
|
|
t1SendFrame.Len
|
|
);
|
|
}
|
|
|
|
//
|
|
// Update the number of bytes that are in the buffer
|
|
// A T1 block is at least 4 bytes long with LRC check and 5 bytes with CRC check
|
|
//
|
|
smartcardRequest->BufferLength +=
|
|
(SmartcardExtension->CardCapabilities.T1.EDC & T1_CRC_CHECK ? 5 : 4) +
|
|
t1SendFrame.Len;
|
|
}
|
|
__finally {
|
|
|
|
#ifdef DEBUG_INTERFACE
|
|
DebugGetT1Request(SmartcardExtension, status);
|
|
#endif
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
#ifdef SMCLIB_VXD
|
|
SMCLIB_SmartcardT1Reply(
|
|
#else
|
|
SmartcardT1Reply(
|
|
#endif
|
|
PSMARTCARD_EXTENSION SmartcardExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies a pointer to the device object for this request.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
T1_BLOCK_FRAME t1RecFrame;
|
|
NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
PIO_HEADER IoHeader = (PIO_HEADER) SmartcardExtension->T1.ReplyData;
|
|
BOOLEAN packetOk = TRUE, chksumOk = TRUE;
|
|
|
|
ASSERT(IoHeader != NULL);
|
|
|
|
if (IoHeader == NULL) {
|
|
|
|
return STATUS_INTERNAL_ERROR;
|
|
}
|
|
|
|
#ifdef DEBUG_INTERFACE
|
|
if (DebugT1Reply(SmartcardExtension)) {
|
|
|
|
// the debugger processed this packet which means
|
|
// that we should not parse it.
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
#endif
|
|
|
|
// signal that we received an answer
|
|
SmartcardExtension->T1.WaitForReply = FALSE;
|
|
|
|
// Invert the data of an inverse convention card
|
|
if (SmartcardExtension->CardCapabilities.InversConvention) {
|
|
|
|
SmartcardInvertData(
|
|
SmartcardExtension->SmartcardReply.Buffer,
|
|
SmartcardExtension->SmartcardReply.BufferLength
|
|
);
|
|
}
|
|
|
|
// Clear waiting time extension
|
|
SmartcardExtension->T1.Wtx = 0;
|
|
|
|
try {
|
|
|
|
ULONG expectedLength =
|
|
SCARD_T1_PROLOGUE_LENGTH +
|
|
SmartcardExtension->SmartcardReply.Buffer[2] +
|
|
(SmartcardExtension->CardCapabilities.T1.EDC & T1_CRC_CHECK ? 2 : 1);
|
|
|
|
if (SmartcardExtension->SmartcardReply.BufferLength < 4 ||
|
|
SmartcardExtension->SmartcardReply.BufferLength != expectedLength) {
|
|
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
(TEXT("%s!SmartcardT1Reply: Packet length incorrect\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
packetOk = FALSE;
|
|
|
|
} else {
|
|
|
|
// calculate the checksum
|
|
chksumOk = SmartcardT1Chksum(
|
|
SmartcardExtension->SmartcardReply.Buffer,
|
|
SmartcardExtension->CardCapabilities.T1.EDC,
|
|
TRUE
|
|
);
|
|
|
|
#if DEBUG
|
|
#ifndef SMCLIB_VXD
|
|
if (SmartcardGetDebugLevel() & DEBUG_T1_TEST) {
|
|
|
|
// inject some checksum errors
|
|
|
|
LARGE_INTEGER Ticks;
|
|
UCHAR RandomVal;
|
|
KeQueryTickCount(&Ticks);
|
|
|
|
RandomVal = (UCHAR) Ticks.LowPart % 4;
|
|
|
|
if (RandomVal == 0) {
|
|
|
|
chksumOk = FALSE;
|
|
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
(TEXT("%s!SmartcardT1Reply: Simulating bad checksum\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
if (chksumOk == FALSE) {
|
|
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
(TEXT("%s!SmartcardT1Reply: Bad checksum\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
}
|
|
|
|
if (packetOk == FALSE || chksumOk == FALSE) {
|
|
|
|
SmartcardExtension->T1.LastError =
|
|
(chksumOk ? T1_ERROR_OTHER : T1_ERROR_CHKSUM);
|
|
|
|
if (SmartcardExtension->T1.OriginalState == 0) {
|
|
|
|
SmartcardExtension->T1.OriginalState =
|
|
SmartcardExtension->T1.State;
|
|
}
|
|
|
|
if (SmartcardExtension->T1.Resend++ == T1_MAX_RETRIES) {
|
|
|
|
SmartcardExtension->T1.Resend = 0;
|
|
|
|
// Try to resynchronize since the resend requests have failed
|
|
SmartcardExtension->T1.State = T1_RESYNCH_REQUEST;
|
|
__leave;
|
|
|
|
}
|
|
|
|
// If the last request was a resynch we try again to resynch
|
|
if (SmartcardExtension->T1.State != T1_RESYNCH_REQUEST) {
|
|
|
|
// Chksum not OK; request resend of last block
|
|
SmartcardExtension->T1.State = T1_R_BLOCK;
|
|
}
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// The checksum of the packet is ok.
|
|
// Now check the rest of the packet
|
|
//
|
|
|
|
// Clear the last error
|
|
SmartcardExtension->T1.LastError = 0;
|
|
|
|
t1RecFrame.Nad = SmartcardExtension->SmartcardReply.Buffer[0];
|
|
t1RecFrame.Pcb = SmartcardExtension->SmartcardReply.Buffer[1];
|
|
t1RecFrame.Len = SmartcardExtension->SmartcardReply.Buffer[2];
|
|
t1RecFrame.Inf = &SmartcardExtension->SmartcardReply.Buffer[3];
|
|
|
|
//
|
|
// If the last block we sent was a ifs request,
|
|
// we expect the card to reply with an ifs response.
|
|
//
|
|
if (SmartcardExtension->T1.State == T1_IFS_REQUEST) {
|
|
|
|
// Check if the card properly responded to an ifs request
|
|
if (t1RecFrame.Pcb == T1_IFS_RESPONSE) {
|
|
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Reply: T1_IFSC_RESPONSE\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
// The smart card acked our ifsd size
|
|
SmartcardExtension->T1.State = T1_I_BLOCK;
|
|
__leave;
|
|
}
|
|
|
|
if ((t1RecFrame.Pcb & 0x82) == 0x82) {
|
|
|
|
//
|
|
// The card does not support ifs request, so we stop
|
|
// sending this and continue with a data block
|
|
// (the card is NOT ISO conform)
|
|
//
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
(TEXT("%s!SmartcardT1Reply: Card does not support IFS REQUEST\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
SmartcardExtension->T1.State = T1_I_BLOCK;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// The card replied with junk to our ifs request.
|
|
// It doesn't make sense to continue.
|
|
//
|
|
status = STATUS_DEVICE_PROTOCOL_ERROR;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// If the last block was a resync. request,
|
|
// we expect the card to answer with a resynch response.
|
|
//
|
|
if (SmartcardExtension->T1.State == T1_RESYNCH_REQUEST) {
|
|
|
|
// Check if the card properly responded to an resynch request
|
|
if (t1RecFrame.Pcb != T1_RESYNCH_RESPONSE) {
|
|
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
(TEXT("%s!SmartcardT1Reply: Card response is not ISO conform! Aborting...\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
status = STATUS_DEVICE_PROTOCOL_ERROR;
|
|
__leave;
|
|
|
|
}
|
|
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Reply: T1_RESYNCH_RESPONSE\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
// Reset error counter
|
|
SmartcardExtension->T1.Resend = 0;
|
|
|
|
// The smart card has successfully responded to a resynch request
|
|
SmartcardExtension->T1.RSN = 0;
|
|
SmartcardExtension->T1.SSN = 0;
|
|
|
|
//
|
|
// Do a complete restart of the whole transmission
|
|
// but without resetting the resynch counter
|
|
//
|
|
SmartcardExtension->T1.State = T1_RESTART;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Now check for other protocol states...
|
|
//
|
|
|
|
//
|
|
// Copy NAD value back to user buffer if this is an extended io-header
|
|
// containing the nad
|
|
//
|
|
if (IoHeader->ScardIoRequest.cbPciLength > sizeof(SCARD_IO_REQUEST) &&
|
|
IoHeader->Asn1Data[0] == 0x81 &&
|
|
IoHeader->Asn1Data[1] == 0x01 &&
|
|
IoHeader->Asn1Data[3] == 0x00) {
|
|
|
|
IoHeader->Asn1Data[2] = t1RecFrame.Nad;
|
|
}
|
|
|
|
if ((t1RecFrame.Pcb & 0x80) == 0) {
|
|
|
|
// This is an I-block
|
|
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Reply: I(%d.%d) <-\n"),
|
|
DRIVER_NAME,
|
|
(t1RecFrame.Pcb & 0x40) >> 6,
|
|
(t1RecFrame.Pcb & 0x20) >> 5)
|
|
);
|
|
|
|
if (((t1RecFrame.Pcb & 0x40) >> 6) == SmartcardExtension->T1.RSN) {
|
|
|
|
// I-Block with correct sequence number
|
|
|
|
PUCHAR data;
|
|
ULONG minBufferSize;
|
|
|
|
// Reset error counter and error indicator
|
|
SmartcardExtension->T1.Resend = 0;
|
|
SmartcardExtension->T1.OriginalState = 0;
|
|
|
|
// We can 'increase' the number of correctly received I-Blocks
|
|
SmartcardExtension->T1.RSN ^= 1;
|
|
|
|
if (SmartcardExtension->T1.State == T1_I_BLOCK) {
|
|
|
|
// This I-Block is also an acknowledge for the I-Block we sent
|
|
SmartcardExtension->T1.SSN ^= 1;
|
|
}
|
|
|
|
// Check size of user buffer
|
|
minBufferSize =
|
|
IoHeader->ScardIoRequest.cbPciLength +
|
|
SmartcardExtension->T1.BytesReceived +
|
|
t1RecFrame.Len;
|
|
|
|
if (SmartcardExtension->IoRequest.ReplyBufferLength < minBufferSize) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
__leave;
|
|
}
|
|
|
|
ASSERT(SmartcardExtension->T1.ReplyData);
|
|
//
|
|
// Let data pointer point behind struct.
|
|
// All reply data will be stored there.
|
|
//
|
|
data =
|
|
SmartcardExtension->T1.ReplyData +
|
|
IoHeader->ScardIoRequest.cbPciLength +
|
|
SmartcardExtension->T1.BytesReceived;
|
|
|
|
// Copy data to user buffer
|
|
RtlCopyMemory(
|
|
data,
|
|
t1RecFrame.Inf,
|
|
t1RecFrame.Len
|
|
);
|
|
|
|
SmartcardExtension->T1.BytesReceived += t1RecFrame.Len;
|
|
|
|
if (t1RecFrame.Pcb & T1_MORE_DATA) {
|
|
|
|
// Ack this block and request the next block
|
|
SmartcardExtension->T1.State = T1_R_BLOCK;
|
|
|
|
} else {
|
|
|
|
//
|
|
// This was the last block of the transmission
|
|
// Set number of bytes returned by this transmission
|
|
//
|
|
*SmartcardExtension->IoRequest.Information =
|
|
IoHeader->ScardIoRequest.cbPciLength +
|
|
SmartcardExtension->T1.BytesReceived;
|
|
|
|
// Copy the result back to the user buffer
|
|
ASSERT(SmartcardExtension->IoRequest.ReplyBuffer != NULL);
|
|
|
|
RtlCopyMemory(
|
|
SmartcardExtension->IoRequest.ReplyBuffer,
|
|
SmartcardExtension->T1.ReplyData,
|
|
IoHeader->ScardIoRequest.cbPciLength +
|
|
SmartcardExtension->T1.BytesReceived
|
|
);
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// I-Block with wrong sequence number
|
|
// We try T1_MAX_RETRIES times to resend the last block.
|
|
// If this is unsuccessfull, we try to resynch.
|
|
// If resynch is unsuccessfull we abort the transmission.
|
|
//
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
(TEXT("%s!SmartcardT1Reply: Block number incorrect\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
SmartcardExtension->T1.LastError = T1_ERROR_OTHER;
|
|
|
|
if (SmartcardExtension->T1.OriginalState == 0) {
|
|
|
|
SmartcardExtension->T1.OriginalState =
|
|
SmartcardExtension->T1.State;
|
|
}
|
|
|
|
if (SmartcardExtension->T1.Resend++ == T1_MAX_RETRIES) {
|
|
|
|
SmartcardExtension->T1.Resend = 0;
|
|
|
|
// Try to resynchronize
|
|
SmartcardExtension->T1.State = T1_RESYNCH_REQUEST;
|
|
__leave;
|
|
}
|
|
|
|
// request the block again.
|
|
SmartcardExtension->T1.State = T1_R_BLOCK;
|
|
__leave;
|
|
}
|
|
|
|
if ((t1RecFrame.Pcb & 0xC0) == 0x80) {
|
|
|
|
// This is an R-block
|
|
|
|
UCHAR RSN = (t1RecFrame.Pcb & 0x10) >> 4;
|
|
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Reply: R(%d) <-\n"),
|
|
DRIVER_NAME,
|
|
RSN)
|
|
);
|
|
|
|
if (RSN != SmartcardExtension->T1.SSN &&
|
|
SmartcardExtension->T1.MoreData) {
|
|
|
|
// The ICC has acked the last block
|
|
SmartcardExtension->T1.Resend = 0;
|
|
|
|
SmartcardExtension->T1.BytesSent += SmartcardExtension->T1.InfBytesSent;
|
|
SmartcardExtension->T1.BytesToSend -= SmartcardExtension->T1.InfBytesSent;
|
|
SmartcardExtension->T1.SSN ^= 1;
|
|
SmartcardExtension->T1.State = T1_I_BLOCK;
|
|
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// We have an error condition...
|
|
//
|
|
|
|
ASSERT(t1RecFrame.Pcb & 0x0f);
|
|
|
|
if ((t1RecFrame.Pcb & 0x02) &&
|
|
SmartcardExtension->T1.State == T1_IFS_REQUEST) {
|
|
|
|
//
|
|
// The card does not support ifs request, so
|
|
// we stop sending this and continue with a data block
|
|
// (the card is NOT ISO conform)
|
|
//
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
(TEXT("%s!SmartcardT1Reply: Card does not support IFS REQUEST\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
SmartcardExtension->T1.State = T1_I_BLOCK;
|
|
__leave;
|
|
}
|
|
|
|
// We have to resend the last block
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
(TEXT("%s!SmartcardT1Reply: Card reports error\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
if (SmartcardExtension->T1.Resend++ == T1_MAX_RETRIES) {
|
|
|
|
SmartcardExtension->T1.Resend = 0;
|
|
|
|
if (SmartcardExtension->T1.OriginalState == 0) {
|
|
|
|
// Save current state
|
|
SmartcardExtension->T1.OriginalState =
|
|
SmartcardExtension->T1.State;
|
|
}
|
|
|
|
// Try to resynchronize
|
|
SmartcardExtension->T1.State = T1_RESYNCH_REQUEST;
|
|
}
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// This is an S-block
|
|
//
|
|
|
|
switch (t1RecFrame.Pcb) {
|
|
|
|
case T1_IFS_REQUEST:
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Reply: T1_IFSC_REQUEST\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
// The smart card wants to exend the IFS - size
|
|
SmartcardExtension->T1.IFSC =
|
|
SmartcardExtension->SmartcardReply.Buffer[3];
|
|
|
|
// Save current state
|
|
ASSERT(SmartcardExtension->T1.OriginalState == 0);
|
|
|
|
SmartcardExtension->T1.OriginalState =
|
|
SmartcardExtension->T1.State;
|
|
|
|
SmartcardExtension->T1.State = T1_IFS_RESPONSE;
|
|
break;
|
|
|
|
case T1_ABORT_REQUEST:
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Reply: T1_ABORT_REQUEST\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
SmartcardExtension->T1.State = T1_ABORT_RESPONSE;
|
|
break;
|
|
|
|
case T1_WTX_REQUEST:
|
|
SmartcardDebug(
|
|
DEBUG_PROTOCOL,
|
|
(TEXT("%s!SmartcardT1Reply: T1_WTX_REQUEST\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
// Smart card needs longer wait time
|
|
SmartcardExtension->T1.Wtx =
|
|
SmartcardExtension->SmartcardReply.Buffer[3];
|
|
|
|
// Save current state
|
|
ASSERT(SmartcardExtension->T1.OriginalState == 0 ||
|
|
SmartcardExtension->T1.OriginalState == T1_WTX_RESPONSE);
|
|
|
|
SmartcardExtension->T1.OriginalState =
|
|
SmartcardExtension->T1.State;
|
|
|
|
SmartcardExtension->T1.State = T1_WTX_RESPONSE;
|
|
break;
|
|
|
|
case T1_VPP_ERROR:
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
(TEXT("%s!SmartcardT1Reply: T1_VPP_ERROR\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
status = STATUS_DEVICE_POWER_FAILURE;
|
|
break;
|
|
|
|
default:
|
|
ASSERTMSG(
|
|
TEXT("SmartcardT1Reply: Invalid Pcb "),
|
|
FALSE
|
|
);
|
|
|
|
status = STATUS_DEVICE_PROTOCOL_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
finally {
|
|
|
|
#if DEBUG
|
|
if (packetOk && chksumOk) {
|
|
|
|
DumpT1Block(
|
|
SmartcardExtension->SmartcardReply.Buffer,
|
|
SmartcardExtension->CardCapabilities.T1.EDC
|
|
);
|
|
}
|
|
#endif
|
|
|
|
if (SmartcardExtension->T1.State == T1_RESYNCH_REQUEST &&
|
|
SmartcardExtension->T1.Resynch++ == T1_MAX_RETRIES) {
|
|
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
(TEXT("%s!SmartcardT1Reply: Too many errors! Aborting...\n"),
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
status = STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
|
|
if (status != STATUS_MORE_PROCESSING_REQUIRED) {
|
|
|
|
if (SmartcardExtension->T1.OriginalState == T1_IFS_REQUEST) {
|
|
|
|
SmartcardExtension->T1.State = T1_IFS_REQUEST;
|
|
|
|
} else {
|
|
|
|
SmartcardExtension->T1.State = T1_START;
|
|
}
|
|
|
|
if (SmartcardExtension->T1.ReplyData) {
|
|
|
|
// free the reply data buffer
|
|
#ifdef SMCLIB_VXD
|
|
_HeapFree(SmartcardExtension->T1.ReplyData, 0);
|
|
#elif defined(SMCLIB_CE)
|
|
LocalFree(SmartcardExtension->T1.ReplyData);
|
|
#else
|
|
|
|
ExFreePool(SmartcardExtension->T1.ReplyData);
|
|
#endif
|
|
SmartcardExtension->T1.ReplyData = NULL;
|
|
}
|
|
SmartcardExtension->T1.OriginalState = 0;
|
|
SmartcardExtension->T1.NAD = 0;
|
|
}
|
|
|
|
}
|
|
return status;
|
|
}
|
|
|
|
#if DEBUG
|
|
#pragma optimize( "", on )
|
|
#endif
|
|
|