Index: ccid-1.4.4/src/ccid.h =================================================================== --- ccid-1.4.4.orig/src/ccid.h 2011-05-13 10:56:01.000000000 +0200 +++ ccid-1.4.4/src/ccid.h 2011-09-02 16:07:03.000000000 +0200 @@ -142,8 +142,10 @@ #define CCID_CLASS_EXCHANGE_MASK 0x00070000 /* Features from bPINSupport */ -#define CCID_CLASS_PIN_VERIFY 0x01 -#define CCID_CLASS_PIN_MODIFY 0x02 +#define CCID_CLASS_PIN_VERIFY 0x01 +#define CCID_CLASS_PIN_MODIFY 0x02 +#define CCID_CLASS_PIN_PACE_CAPABILITIES 0x10 +#define CCID_CLASS_PIN_PACE_EXECUTE 0x20 /* See CCID specs ch. 4.2.1 */ #define CCID_ICC_PRESENT_ACTIVE 0x00 /* 00 0000 00 */ @@ -154,6 +156,25 @@ #define CCID_COMMAND_FAILED 0x40 /* 01 0000 00 */ #define CCID_TIME_EXTENSION 0x80 /* 10 0000 00 */ +#define PACE_SUCCESS 0x00000000 +#define PACE_ERROR_LENGTH_INCONSISTENT 0xD0000001 +#define PACE_ERROR_UNEXPECTED_DATA 0xD0000002 +#define PACE_ERROR_UNEXPECTED_DATA_COMBINATION 0xD0000003 +#define PACE_ERROR_CARD_NOT_SUPPORTED 0xE0000001 +#define PACE_ERROR_ALGORITH_NOT_SUPPORTED 0xE0000002 +#define PACE_ERROR_PINID_NOT_SUPPORTED 0xE0000003 +#define PACE_ERROR_SELECT_EF_CARDACCESS 0xF0000000 +#define PACE_ERROR_READ_BINARY 0xF0010000 +#define PACE_ERROR_MSE_SET_AT 0xF0020000 +#define PACE_ERROR_GENERAL_AUTHENTICATE_1 0xF0030000 +#define PACE_ERROR_GENERAL_AUTHENTICATE_2 0xF0040000 +#define PACE_ERROR_GENERAL_AUTHENTICATE_3 0xF0050000 +#define PACE_ERROR_GENERAL_AUTHENTICATE_4 0xF0060000 +#define PACE_ERROR_COMMUNICATION 0xF0100001 +#define PACE_ERROR_NO_CARD 0xF0100002 +#define PACE_ERROR_ABORTED 0xF0200001 +#define PACE_ERROR_TIMEOUT 0xF0200002 + /* bInterfaceProtocol for ICCD */ #define PROTOCOL_CCID 0 /* plain CCID */ #define PROTOCOL_ICCD_A 1 /* ICCD Version A */ @@ -230,6 +251,8 @@ /* convert a 4 byte integer in USB format into an int */ #define dw2i(a, x) (unsigned int)((((((a[x+3] << 8) + a[x+2]) << 8) + a[x+1]) << 8) + a[x]) +/* convert a 2 byte integer in USB format into an int */ +#define w2i(a, x) (unsigned int)(((a[x+1]) << 8) + a[x]) /* all the data rates specified by ISO 7816-3 Fi/Di tables */ #define ISO_DATA_RATES 10753, 14337, 15625, 17204, \ Index: ccid-1.4.4/src/ccid_ifdhandler.h =================================================================== --- ccid-1.4.4.orig/src/ccid_ifdhandler.h 2010-06-04 14:31:15.000000000 +0200 +++ ccid-1.4.4/src/ccid_ifdhandler.h 2011-09-02 16:07:03.000000000 +0200 @@ -59,4 +59,9 @@ CcidDesc *get_ccid_slot(unsigned int reader_index); #endif +#ifndef FEATURE_EXECUTE_PACE +#define FEATURE_EXECUTE_PACE 0x20 +#endif +#define IOCTL_FEATURE_EXECUTE_PACE \ + SCARD_CTL_CODE(FEATURE_EXECUTE_PACE + CLASS2_IOCTL_MAGIC) Index: ccid-1.4.4/src/commands.c =================================================================== --- ccid-1.4.4.orig/src/commands.c 2011-02-22 23:20:26.000000000 +0100 +++ ccid-1.4.4/src/commands.c 2011-09-02 16:07:03.000000000 +0200 @@ -75,6 +75,18 @@ unsigned int tx_length, unsigned char tx_buffer[], unsigned int *rx_length, unsigned char rx_buffer[]); +static RESPONSECODE SecurePINPACE(unsigned int reader_index, + unsigned char TxBuffer[], unsigned int TxLength, + unsigned char RxBuffer[], unsigned int *RxLength, + unsigned char bPINOperation); + +static RESPONSECODE transform_EstablishPACEChannel_InputData( + unsigned char input[], unsigned int input_length); + +static RESPONSECODE transform_EstablishPACEChannel_OutputData( + unsigned char output[], unsigned int output_length); + +static void i2w(uint16_t value, unsigned char *buffer); static void i2dw(int value, unsigned char *buffer); static unsigned int bei2i(unsigned char *buffer); @@ -753,6 +765,341 @@ return ret; } /* SecurePINModify */ +static RESPONSECODE transform_EstablishPACEChannel_InputData( + unsigned char input[], unsigned int input_length) +{ + uint8_t lengthCHAT, lengthPIN; + uint16_t lengthCertificateDescription; + size_t parsed = 0; + + /* transform length fields of input data to little endian */ + + if (input_length < parsed+1) { + DEBUG_CRITICAL("Malformed Establish PACE Channel input data."); + return IFD_COMMUNICATION_ERROR; + } + switch (input[parsed]) { + case 0x01: + DEBUG_COMM("Initiating PACE with MRZ"); + break; + case 0x02: + DEBUG_COMM("Initiating PACE with CAN"); + break; + case 0x03: + DEBUG_COMM("Initiating PACE with PIN"); + break; + case 0x04: + DEBUG_COMM("Initiating PACE with PUK"); + break; + default: + DEBUG_COMM("Initiating PACE with unknown PACE secret type"); + break; + } + parsed += 1; + + if (input_length < parsed+1) { + DEBUG_CRITICAL("Malformed Establish PACE Channel input data."); + return IFD_COMMUNICATION_ERROR; + } + lengthCHAT = input[parsed]; + parsed += 1; + + if (input_length < parsed+lengthCHAT) { + DEBUG_CRITICAL("Malformed Establish PACE Channel input data."); + return IFD_COMMUNICATION_ERROR; + } + if (lengthCHAT) + DEBUG_INFO_XXD("CHAT:\n", &input[parsed], lengthCHAT); + parsed += lengthCHAT; + + if (input_length < parsed+1) { + DEBUG_CRITICAL("Malformed Establish PACE Channel input data."); + return IFD_COMMUNICATION_ERROR; + } + lengthPIN = input[parsed]; + parsed += 1; + + if (input_length < parsed+lengthPIN) { + DEBUG_CRITICAL("Malformed Establish PACE Channel input data."); + return IFD_COMMUNICATION_ERROR; + } +#if 0 + /* dont print secrets... */ + if (lengthPIN) + DEBUG_INFO_XXD("PIN:\n", &input[parsed], lengthPIN); +#endif + parsed += lengthPIN; + + if (input_length < parsed+2) { + DEBUG_CRITICAL("Malformed Establish PACE Channel input data."); + return IFD_COMMUNICATION_ERROR; + } + memcpy(&lengthCertificateDescription, &input[parsed], 2); + i2w(lengthCertificateDescription, &input[parsed]); + parsed += 2; + + if (input_length < parsed+lengthCertificateDescription) { + DEBUG_CRITICAL("Malformed Establish PACE Channel input data."); + return IFD_COMMUNICATION_ERROR; + } + if (lengthCertificateDescription) + DEBUG_INFO_XXD("Certificate description:\n", &input[parsed], lengthCertificateDescription); + parsed += lengthCertificateDescription; + + if (parsed < input_length) { + DEBUG_CRITICAL2("Overrun by %u bytes", input_length - parsed); + return IFD_COMMUNICATION_ERROR; + } + + return IFD_SUCCESS; +} + +static RESPONSECODE transform_EstablishPACEChannel_OutputData( + unsigned char output[], unsigned int output_length) +{ + uint8_t lengthCAR, lengthCARprev; + uint16_t lengthOutputData, lengthEF_CardAccess, length_IDicc; + uint32_t result; + size_t parsed = 0; + + if (parsed+4 > output_length) { + DEBUG_CRITICAL("Malformed Establish PACE Channel output data."); + return IFD_COMMUNICATION_ERROR; + } + result = dw2i(output, parsed); + memcpy(&output[parsed], &result, 4); + DEBUG_INFO2("EstablishPACEChannel Result: %08X", result); + parsed += 4; + + if (parsed+2 > output_length) { + DEBUG_CRITICAL("Malformed Establish PACE Channel output data."); + return IFD_COMMUNICATION_ERROR; + } + lengthOutputData = w2i(output, parsed); + if ((unsigned int)lengthOutputData+6 != output_length) { + DEBUG_CRITICAL("Malformed Establish PACE Channel output data."); + return IFD_COMMUNICATION_ERROR; + } + parsed += 2; + + if (parsed+2 > output_length) { + DEBUG_CRITICAL("Malformed Establish PACE Channel output data."); + return IFD_COMMUNICATION_ERROR; + } + DEBUG_INFO3("MSE:Set AT Statusbytes: %02X %02X", + output[parsed+0], output[parsed+1]); + parsed += 2; + + if (parsed+2 > output_length) { + DEBUG_CRITICAL("Malformed Establish PACE Channel output data."); + return IFD_COMMUNICATION_ERROR; + } + lengthEF_CardAccess = w2i(output, parsed); + memcpy(&output[parsed], &lengthEF_CardAccess, 2); + parsed += 2; + + if (parsed+lengthEF_CardAccess > output_length) { + DEBUG_CRITICAL("Malformed Establish PACE Channel output data."); + return IFD_COMMUNICATION_ERROR; + } + if (lengthEF_CardAccess) + DEBUG_INFO_XXD("EF.CardAccess:\n", &output[parsed], lengthEF_CardAccess); + parsed += lengthEF_CardAccess; + + if (parsed+1 > output_length) { + DEBUG_CRITICAL("Malformed Establish PACE Channel output data."); + return IFD_COMMUNICATION_ERROR; + } + lengthCAR = output[parsed]; + parsed += 1; + + if (parsed+lengthCAR > output_length) { + DEBUG_CRITICAL("Malformed Establish PACE Channel output data."); + return IFD_COMMUNICATION_ERROR; + } + if (lengthCAR) + DEBUG_INFO_XXD("Recent Certificate Authority:\n", + &output[parsed], lengthCAR); + parsed += lengthCAR; + + if (parsed+1 > output_length) { + DEBUG_CRITICAL("Malformed Establish PACE Channel output data."); + return IFD_COMMUNICATION_ERROR; + } + lengthCARprev = output[parsed]; + parsed += 1; + + if (parsed+lengthCARprev > output_length) { + DEBUG_CRITICAL("Malformed Establish PACE Channel output data."); + return IFD_COMMUNICATION_ERROR; + } + if (lengthCARprev) + DEBUG_INFO_XXD("Previous Certificate Authority:\n", + &output[parsed], lengthCARprev); + parsed += lengthCARprev; + + if (parsed+2 > output_length) { + DEBUG_CRITICAL("Malformed Establish PACE Channel output data."); + return IFD_COMMUNICATION_ERROR; + } + length_IDicc = w2i(output, parsed); + memcpy(&output[parsed], &length_IDicc, 2); + parsed += 2; + + if (parsed+length_IDicc > output_length) { + DEBUG_CRITICAL("Malformed Establish PACE Channel output data."); + return IFD_COMMUNICATION_ERROR; + } + if (length_IDicc) + DEBUG_INFO_XXD("IDicc:\n", &output[parsed], length_IDicc); + parsed += length_IDicc; + + if (parsed < output_length) { + DEBUG_CRITICAL2("Overrun by %u bytes", output_length - parsed); + return IFD_COMMUNICATION_ERROR; + } + + return IFD_SUCCESS; +} + +RESPONSECODE SecurePINPACEVerify(unsigned int reader_index, + unsigned char TxBuffer[], unsigned int TxLength, + unsigned char RxBuffer[], unsigned int *RxLength) +{ + /* bPINOperation: PIN PACE Capabilities */ + RESPONSECODE return_value; + int old_read_timeout; + _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index); + + old_read_timeout= ccid_descriptor -> readTimeout; + ccid_descriptor -> readTimeout = 30*1000; /* 30 seconds */ + + return_value = transform_EstablishPACEChannel_InputData(TxBuffer, TxLength); + if (IFD_SUCCESS != return_value) + goto err; + + return_value = SecurePINPACE(reader_index, TxBuffer, TxLength, RxBuffer, + RxLength, CCID_CLASS_PIN_PACE_EXECUTE); + if (IFD_SUCCESS != return_value) + goto err; + + if (*RxLength < 6) { + DEBUG_CRITICAL("Malformed Establish PACE output buffer."); + return_value = IFD_COMMUNICATION_ERROR; + goto err; + } + + return_value = transform_EstablishPACEChannel_OutputData(RxBuffer, *RxLength); + if (IFD_SUCCESS != return_value) + goto err; + +err: + ccid_descriptor -> readTimeout = old_read_timeout; + return return_value; +} + +RESPONSECODE SecurePINPACECapabilities(unsigned int reader_index, + unsigned char TxBuffer[], unsigned int TxLength, + unsigned char RxBuffer[], unsigned int *RxLength) +{ + /* bPINOperation: PIN PACE Capabilities */ + if (*RxLength < 6) { + DEBUG_CRITICAL2("overrun by %d bytes", 6 - *RxLength); + return IFD_ERROR_INSUFFICIENT_BUFFER; + } + + unsigned int length = *RxLength - 6; + RESPONSECODE r = SecurePINPACE(reader_index, TxBuffer, TxLength, + RxBuffer+6, &length, CCID_CLASS_PIN_PACE_CAPABILITIES); + if (r != IFD_SUCCESS) + return r; + + uint32_t s = PACE_SUCCESS; + memcpy(RxBuffer, &s, 4); /* Result */ + memcpy(RxBuffer+4, &length, 2); /* lengthOutputData */ + *RxLength = length + 6; + + return IFD_SUCCESS; +} + +static RESPONSECODE SecurePINPACE(unsigned int reader_index, + unsigned char TxBuffer[], unsigned int TxLength, + unsigned char RxBuffer[], unsigned int *RxLength, + unsigned char bPINOperation) +{ + unsigned char cmd[1024]; + unsigned int length; + _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index); + status_t res; + + cmd[0] = 0x69; /* Secure */ + i2dw(TxLength, cmd+1); + cmd[5] = ccid_descriptor->bCurrentSlotIndex; + cmd[6] = (*ccid_descriptor->pbSeq)++; + cmd[7] = 0; + cmd[8] = 0; + cmd[9] = 0; + cmd[10] = bPINOperation; + + if (sizeof(cmd) < 11+TxLength) + { + DEBUG_CRITICAL2("Too much data in input (overrun by %d bytes)", + 11+TxLength - sizeof(cmd)); + return IFD_COMMUNICATION_ERROR; + } + memcpy(cmd+11, TxBuffer, TxLength); + + res = WritePort(reader_index, 11+TxLength, cmd); + if (res != STATUS_SUCCESS) + { + if (STATUS_NO_SUCH_DEVICE == res) + return IFD_NO_SUCH_DEVICE; + return IFD_COMMUNICATION_ERROR; + } + + length = sizeof(cmd); + res = ReadPort(reader_index, &length, cmd); + if (res != STATUS_SUCCESS) + { + if (STATUS_NO_SUCH_DEVICE == res) + return IFD_NO_SUCH_DEVICE; + return IFD_COMMUNICATION_ERROR; + } + + if (length < STATUS_OFFSET+1) + { + DEBUG_CRITICAL2("Not enough data received: %d bytes", length); + return IFD_COMMUNICATION_ERROR; + } + + if (cmd[STATUS_OFFSET] & CCID_COMMAND_FAILED) + { + ccid_error(cmd[ERROR_OFFSET], __FILE__, __LINE__, __FUNCTION__); /* bError */ + return IFD_COMMUNICATION_ERROR; + } + + /* we have read less (or more) data than the CCID frame says to contain */ + if (length-10 != dw2i(cmd, 1)) + { + DEBUG_CRITICAL3("Can't read all data (%d out of %d expected)", + length-10, dw2i(cmd, 1)); + return IFD_COMMUNICATION_ERROR; + } + + length = dw2i(cmd, 1); + if (length <= *RxLength) + *RxLength = length; + else + { + DEBUG_CRITICAL2("overrun by %d bytes", length - *RxLength); + return IFD_ERROR_INSUFFICIENT_BUFFER; + } + + memcpy(RxBuffer, cmd+10, *RxLength); + + return IFD_SUCCESS; +} + /***************************************************************************** * @@ -2155,6 +2502,18 @@ } /* i2dw */ /***************************************************************************** + * + * i2w + * + ****************************************************************************/ +static void i2w(uint16_t value, unsigned char buffer[]) +{ + buffer[0] = value & 0xFF; + buffer[1] = (value >> 8) & 0xFF; +} /* i2w */ + + +/***************************************************************************** * * bei2i (big endian integer to host order interger) * Index: ccid-1.4.4/src/commands.h =================================================================== --- ccid-1.4.4.orig/src/commands.h 2010-06-04 14:31:15.000000000 +0200 +++ ccid-1.4.4/src/commands.h 2011-09-02 16:07:03.000000000 +0200 @@ -37,6 +37,14 @@ unsigned char TxBuffer[], unsigned int TxLength, unsigned char RxBuffer[], unsigned int *RxLength); +RESPONSECODE SecurePINPACECapabilities(unsigned int reader_index, + unsigned char TxBuffer[], unsigned int TxLength, + unsigned char RxBuffer[], unsigned int *RxLength); + +RESPONSECODE SecurePINPACEVerify(unsigned int reader_index, + unsigned char TxBuffer[], unsigned int TxLength, + unsigned char RxBuffer[], unsigned int *RxLength); + RESPONSECODE CmdEscape(unsigned int reader_index, const unsigned char TxBuffer[], unsigned int TxLength, unsigned char RxBuffer[], unsigned int *RxLength); Index: ccid-1.4.4/src/ifdhandler.c =================================================================== --- ccid-1.4.4.orig/src/ifdhandler.c 2011-05-11 11:46:00.000000000 +0200 +++ ccid-1.4.4/src/ifdhandler.c 2011-09-02 16:12:56.000000000 +0200 @@ -65,6 +65,73 @@ static unsigned int T1_card_timeout(double f, double d, int TC1, int BWI, int CWI, int clock_frequency); static int get_IFSC(ATR_t *atr, int *i); +static RESPONSECODE do_feature_execute_pace(unsigned int reader_index, + PUCHAR TxBuffer, DWORD TxLength, PUCHAR RxBuffer, DWORD RxLength, + PDWORD pdwBytesReturned); + +static RESPONSECODE do_feature_execute_pace(unsigned int reader_index, + PUCHAR TxBuffer, DWORD TxLength, PUCHAR RxBuffer, DWORD RxLength, + PDWORD pdwBytesReturned) +{ + uint16_t lengthInputData; + unsigned int i; + RESPONSECODE r; + + if (TxLength < 3 || !TxBuffer) { + DEBUG_CRITICAL("Buffer too small, could not determine length of " + "input data"); + i = PACE_ERROR_LENGTH_INCONSISTENT; + goto err; + } + + memcpy(&lengthInputData, &TxBuffer[1], 2); + + if (TxLength != 3+lengthInputData) { + DEBUG_CRITICAL3("Buffer too small or too big to contain only " + "function index and input data (expected %u, got %u)", + 3+lengthInputData, TxLength); + i = PACE_ERROR_LENGTH_INCONSISTENT; + goto err; + } + + switch(*TxBuffer) { + case 1: + i = RxLength; + r = SecurePINPACECapabilities(reader_index, + TxBuffer+3, lengthInputData, RxBuffer, &i); + *pdwBytesReturned = i; + return r; + + case 2: + i = RxLength; + r = SecurePINPACEVerify(reader_index, TxBuffer+3, + lengthInputData, RxBuffer, &i); + *pdwBytesReturned = i; + return r; + + default: + DEBUG_CRITICAL2("Unknown PACE function %u", + (unsigned char)*TxBuffer); + if (RxLength < 6 || !RxBuffer) { + DEBUG_CRITICAL2("Need %u more bytes in RxBuffer", + RxLength>6 ? (unsigned int)6-RxLength : 0); + return IFD_COMMUNICATION_ERROR; + } + + i = PACE_ERROR_UNEXPECTED_DATA; + goto err; + } + +err: + memcpy(RxBuffer, &i, 4); + memset(RxBuffer + 4, 0, 2); + + *pdwBytesReturned = 6; + + return IFD_SUCCESS; +} + + EXTERNAL RESPONSECODE IFDHCreateChannelByName(DWORD Lun, LPSTR lpcDevice) @@ -1400,6 +1467,17 @@ iBytesReturned += sizeof(PCSC_TLV_STRUCTURE); } + if (ccid_descriptor -> bPINSupport & CCID_CLASS_PIN_PACE_CAPABILITIES) + { + pcsc_tlv -> tag = FEATURE_EXECUTE_PACE; + pcsc_tlv -> length = 0x04; /* always 0x04 */ + pcsc_tlv -> value = htonl(IOCTL_FEATURE_EXECUTE_PACE); + + pcsc_tlv++; + iBytesReturned += sizeof(PCSC_TLV_STRUCTURE); + } + + /* We can always forward wLcdLayout */ pcsc_tlv -> tag = FEATURE_IFD_PIN_PROPERTIES; pcsc_tlv -> length = 0x04; /* always 0x04 */ @@ -1582,6 +1660,12 @@ } } + if (IOCTL_FEATURE_EXECUTE_PACE == dwControlCode) + { + return_value = do_feature_execute_pace(reader_index, TxBuffer, + TxLength, RxBuffer, RxLength, pdwBytesReturned); + } + if (IFD_SUCCESS != return_value) *pdwBytesReturned = 0;