/* ifdhandler.c: IFDH API Copyright (C) 2003-2005 Ludovic Rousseau This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* $Id: ifdhandler.c 2648 2007-10-11 13:41:35Z rousseau $ */ #include #include #include #include #include "misc.h" #include "config.h" #include #include #include #include "ccid.h" #include "defs.h" #include "ccid_ifdhandler.h" #include "debug.h" #include "utils.h" #include "commands.h" #include "towitoko/atr.h" #include "towitoko/pps.h" #include "parser.h" #ifdef HAVE_PTHREAD #include #endif /* Array of structures to hold the ATR and other state value of each slot */ static CcidDesc CcidSlots[CCID_DRIVER_MAX_READERS]; /* global mutex */ #ifdef HAVE_PTHREAD static pthread_mutex_t ifdh_context_mutex = PTHREAD_MUTEX_INITIALIZER; #endif int LogLevel = DEBUG_LEVEL_CRITICAL | DEBUG_LEVEL_INFO; int DriverOptions = 0; int PowerOnVoltage = VOLTAGE_5V; static int DebugInitialized = FALSE; /* local functions */ static void init_driver(void); static void extra_egt(ATR_t *atr, _ccid_descriptor *ccid_desc, DWORD Protocol); static char find_baud_rate(unsigned int baudrate, unsigned int *list); static unsigned int T0_card_timeout(double f, double d, int TC1, int TC2, int clock_frequency); static unsigned int T1_card_timeout(double f, double d, int TC1, int BWI, int CWI, int clock_frequency); EXTERNAL RESPONSECODE IFDHCreateChannelByName(DWORD Lun, LPSTR lpcDevice) { RESPONSECODE return_value = IFD_SUCCESS; int reader_index; if (! DebugInitialized) init_driver(); DEBUG_INFO3("lun: %X, device: %s", Lun, lpcDevice); if (-1 == (reader_index = GetNewReaderIndex(Lun))) return IFD_COMMUNICATION_ERROR; /* Reset ATR buffer */ CcidSlots[reader_index].nATRLength = 0; *CcidSlots[reader_index].pcATRBuffer = '\0'; /* Reset PowerFlags */ CcidSlots[reader_index].bPowerFlags = POWERFLAGS_RAZ; #ifdef HAVE_PTHREAD pthread_mutex_lock(&ifdh_context_mutex); #endif if (OpenPortByName(reader_index, lpcDevice) != STATUS_SUCCESS) { DEBUG_CRITICAL("failed"); return_value = IFD_COMMUNICATION_ERROR; /* release the allocated reader_index */ ReleaseReaderIndex(reader_index); } else { /* Maybe we have a special treatment for this reader */ ccid_open_hack(reader_index); /* Try to access the reader */ /* This "warm up" sequence is sometimes needed when pcscd is * restarted with the reader already connected. We get some * "usb_bulk_read: Resource temporarily unavailable" on the first * few tries. It is an empirical hack */ if ((IFD_COMMUNICATION_ERROR == IFDHICCPresence(Lun)) && (IFD_COMMUNICATION_ERROR == IFDHICCPresence(Lun)) && (IFD_COMMUNICATION_ERROR == IFDHICCPresence(Lun))) { DEBUG_CRITICAL("failed"); return_value = IFD_COMMUNICATION_ERROR; /* release the allocated reader_index */ ReleaseReaderIndex(reader_index); } } #ifdef HAVE_PTHREAD pthread_mutex_unlock(&ifdh_context_mutex); #endif return return_value; } /* IFDHCreateChannelByName */ EXTERNAL RESPONSECODE IFDHCreateChannel(DWORD Lun, DWORD Channel) { /* * Lun - Logical Unit Number, use this for multiple card slots or * multiple readers. 0xXXXXYYYY - XXXX multiple readers, YYYY multiple * slots. The resource manager will set these automatically. By * default the resource manager loads a new instance of the driver so * if your reader does not have more than one smartcard slot then * ignore the Lun in all the functions. Future versions of PC/SC might * support loading multiple readers through one instance of the driver * in which XXXX would be important to implement if you want this. */ /* * Channel - Channel ID. This is denoted by the following: 0x000001 - * /dev/pcsc/1 0x000002 - /dev/pcsc/2 0x000003 - /dev/pcsc/3 * * USB readers may choose to ignore this parameter and query the bus * for the particular reader. */ /* * This function is required to open a communications channel to the * port listed by Channel. For example, the first serial reader on * COM1 would link to /dev/pcsc/1 which would be a sym link to * /dev/ttyS0 on some machines This is used to help with intermachine * independance. * * Once the channel is opened the reader must be in a state in which * it is possible to query IFDHICCPresence() for card status. * * returns: * * IFD_SUCCESS IFD_COMMUNICATION_ERROR */ RESPONSECODE return_value = IFD_SUCCESS; int reader_index; if (! DebugInitialized) init_driver(); DEBUG_INFO2("lun: %X", Lun); if (-1 == (reader_index = GetNewReaderIndex(Lun))) return IFD_COMMUNICATION_ERROR; /* Reset ATR buffer */ CcidSlots[reader_index].nATRLength = 0; *CcidSlots[reader_index].pcATRBuffer = '\0'; /* Reset PowerFlags */ CcidSlots[reader_index].bPowerFlags = POWERFLAGS_RAZ; #ifdef HAVE_PTHREAD pthread_mutex_lock(&ifdh_context_mutex); #endif if (OpenPort(reader_index, Channel) != STATUS_SUCCESS) { DEBUG_CRITICAL("failed"); return_value = IFD_COMMUNICATION_ERROR; /* release the allocated reader_index */ ReleaseReaderIndex(reader_index); } else /* Maybe we have a special treatment for this reader */ ccid_open_hack(reader_index); #ifdef HAVE_PTHREAD pthread_mutex_unlock(&ifdh_context_mutex); #endif return return_value; } /* IFDHCreateChannel */ EXTERNAL RESPONSECODE IFDHCloseChannel(DWORD Lun) { /* * This function should close the reader communication channel for the * particular reader. Prior to closing the communication channel the * reader should make sure the card is powered down and the terminal * is also powered down. * * returns: * * IFD_SUCCESS IFD_COMMUNICATION_ERROR */ int reader_index; DEBUG_INFO2("lun: %X", Lun); if (-1 == (reader_index = LunToReaderIndex(Lun))) return IFD_COMMUNICATION_ERROR; /* Restore the default timeout * No need to wait too long if the reader disapeared */ get_ccid_descriptor(reader_index)->readTimeout = DEFAULT_COM_READ_TIMEOUT; (void)CmdPowerOff(reader_index); /* No reader status check, if it failed, what can you do ? :) */ #ifdef HAVE_PTHREAD pthread_mutex_lock(&ifdh_context_mutex); #endif (void)ClosePort(reader_index); ReleaseReaderIndex(reader_index); #ifdef HAVE_PTHREAD pthread_mutex_unlock(&ifdh_context_mutex); #endif return IFD_SUCCESS; } /* IFDHCloseChannel */ EXTERNAL RESPONSECODE IFDHGetCapabilities(DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value) { /* * This function should get the slot/card capabilities for a * particular slot/card specified by Lun. Again, if you have only 1 * card slot and don't mind loading a new driver for each reader then * ignore Lun. * * Tag - the tag for the information requested example: TAG_IFD_ATR - * return the Atr and it's size (required). these tags are defined in * ifdhandler.h * * Length - the length of the returned data Value - the value of the * data * * returns: * * IFD_SUCCESS IFD_ERROR_TAG */ int reader_index; DEBUG_INFO3("lun: %X, tag: 0x%X", Lun, Tag); if (-1 == (reader_index = LunToReaderIndex(Lun))) return IFD_COMMUNICATION_ERROR; switch (Tag) { case TAG_IFD_ATR: case SCARD_ATTR_ATR_STRING: /* If Length is not zero, powerICC has been performed. * Otherwise, return NULL pointer * Buffer size is stored in *Length */ *Length = (*Length < CcidSlots[reader_index].nATRLength) ? *Length : CcidSlots[reader_index].nATRLength; if (*Length) memcpy(Value, CcidSlots[reader_index].pcATRBuffer, *Length); break; #ifdef HAVE_PTHREAD case TAG_IFD_SIMULTANEOUS_ACCESS: if (*Length >= 1) { *Length = 1; *Value = CCID_DRIVER_MAX_READERS; } break; case TAG_IFD_THREAD_SAFE: if (*Length >= 1) { *Length = 1; *Value = 1; /* Can talk to multiple readers at the same time */ } break; #endif case TAG_IFD_SLOTS_NUMBER: if (*Length >= 1) { *Length = 1; *Value = 1 + get_ccid_descriptor(reader_index) -> bMaxSlotIndex; DEBUG_INFO2("Reader supports %d slot(s)", *Value); } break; case TAG_IFD_SLOT_THREAD_SAFE: if (*Length >= 1) { *Length = 1; *Value = 0; /* Can NOT talk to multiple slots at the same time */ } break; case SCARD_ATTR_VENDOR_IFD_VERSION: /* Vendor-supplied interface device version (DWORD in the form * 0xMMmmbbbb where MM = major version, mm = minor version, and * bbbb = build number). */ *Length = sizeof(DWORD); if (Value) *(DWORD *)Value = CCID_VERSION; break; case SCARD_ATTR_VENDOR_NAME: #define VENDOR_NAME "Ludovic Rousseau" *Length = sizeof(VENDOR_NAME); if (Value) memcpy(Value, VENDOR_NAME, sizeof(VENDOR_NAME)); break; case SCARD_ATTR_MAXINPUT: *Length = sizeof(uint32_t); if (Value) *(uint32_t *)Value = get_ccid_descriptor(reader_index) -> dwMaxCCIDMessageLength -10; break; default: return IFD_ERROR_TAG; } return IFD_SUCCESS; } /* IFDHGetCapabilities */ EXTERNAL RESPONSECODE IFDHSetCapabilities(DWORD Lun, DWORD Tag, /*@unused@*/ DWORD Length, /*@unused@*/ PUCHAR Value) { /* * This function should set the slot/card capabilities for a * particular slot/card specified by Lun. Again, if you have only 1 * card slot and don't mind loading a new driver for each reader then * ignore Lun. * * Tag - the tag for the information needing set * * Length - the length of the returned data Value - the value of the * data * * returns: * * IFD_SUCCESS IFD_ERROR_TAG IFD_ERROR_SET_FAILURE * IFD_ERROR_VALUE_READ_ONLY */ /* By default, say it worked */ DEBUG_INFO3("lun: %X, tag: 0x%X", Lun, Tag); /* if (CheckLun(Lun)) return IFD_COMMUNICATION_ERROR; */ return IFD_NOT_SUPPORTED; } /* IFDHSetCapabilities */ EXTERNAL RESPONSECODE IFDHSetProtocolParameters(DWORD Lun, DWORD Protocol, UCHAR Flags, UCHAR PTS1, UCHAR PTS2, UCHAR PTS3) { /* * This function should set the PTS of a particular card/slot using * the three PTS parameters sent * * Protocol - 0 .... 14 T=0 .... T=14 * Flags - Logical OR of possible values: * IFD_NEGOTIATE_PTS1 * IFD_NEGOTIATE_PTS2 * IFD_NEGOTIATE_PTS3 * to determine which PTS values to negotiate. * PTS1,PTS2,PTS3 - PTS Values. * * returns: * IFD_SUCCESS * IFD_ERROR_PTS_FAILURE * IFD_COMMUNICATION_ERROR * IFD_PROTOCOL_NOT_SUPPORTED */ BYTE pps[PPS_MAX_LENGTH]; ATR_t atr; unsigned int len; int convention; int reader_index; /* Set ccid desc params */ CcidDesc *ccid_slot; _ccid_descriptor *ccid_desc; DEBUG_INFO3("lun: %X, protocol T=%d", Lun, Protocol-1); if (-1 == (reader_index = LunToReaderIndex(Lun))) return IFD_COMMUNICATION_ERROR; /* Set to zero buffer */ memset(pps, 0, sizeof(pps)); memset(&atr, 0, sizeof(atr)); /* Get ccid params */ ccid_slot = get_ccid_slot(reader_index); ccid_desc = get_ccid_descriptor(reader_index); /* Do not send CCID command SetParameters or PPS to the CCID * The CCID will do this himself */ if (ccid_desc->dwFeatures & CCID_CLASS_AUTO_PPS_PROP) return IFD_SUCCESS; /* Get ATR of the card */ ATR_InitFromArray(&atr, ccid_slot->pcATRBuffer, ccid_slot->nATRLength); /* Apply Extra EGT patch for bogus cards */ extra_egt(&atr, ccid_desc, Protocol); if (SCARD_PROTOCOL_T0 == Protocol) pps[1] |= ATR_PROTOCOL_TYPE_T0; else if (SCARD_PROTOCOL_T1 == Protocol) pps[1] |= ATR_PROTOCOL_TYPE_T1; else return IFD_PROTOCOL_NOT_SUPPORTED; /* TA2 present -> specific mode */ if (atr.ib[1][ATR_INTERFACE_BYTE_TA].present) { if (pps[1] != (atr.ib[1][ATR_INTERFACE_BYTE_TA].value & 0x0F)) { /* wrong protocol */ DEBUG_COMM3("Specific mode in T=%d and T=%d requested", atr.ib[1][ATR_INTERFACE_BYTE_TA].value & 0x0F, pps[1]); return IFD_PROTOCOL_NOT_SUPPORTED; } } /* TCi (i>2) indicates CRC instead of LRC */ if (SCARD_PROTOCOL_T1 == Protocol) { t1_state_t *t1 = &(ccid_slot -> t1); int i; /* TCi (i>2) present? */ for (i=2; i2) must be used */ break; } } /* PTS1? */ if (Flags & IFD_NEGOTIATE_PTS1) { /* just use the value passed in argument */ pps[1] |= 0x10; /* PTS1 presence */ pps[2] = PTS1; } else { /* TA1 present */ if (atr.ib[0][ATR_INTERFACE_BYTE_TA].present) { unsigned int card_baudrate; unsigned int default_baudrate; double f, d; ATR_GetParameter(&atr, ATR_PARAMETER_D, &d); ATR_GetParameter(&atr, ATR_PARAMETER_F, &f); /* may happen with non ISO cards */ if ((0 == f) || (0 == d)) { /* values for TA1=11 */ f = 372; d = 1; } /* Baudrate = f x D/F */ card_baudrate = (unsigned int) (1000 * ccid_desc->dwDefaultClock * d / f); default_baudrate = (unsigned int) (1000 * ccid_desc->dwDefaultClock * ATR_DEFAULT_D / ATR_DEFAULT_F); /* if the card does not try to lower the default speed */ if ((card_baudrate > default_baudrate) /* and the reader is fast enough */ && (card_baudrate <= ccid_desc->dwMaxDataRate)) { /* the reader has no baud rates table */ if ((NULL == ccid_desc->arrayOfSupportedDataRates) /* or explicitely support it */ || find_baud_rate(card_baudrate, ccid_desc->arrayOfSupportedDataRates)) { pps[1] |= 0x10; /* PTS1 presence */ pps[2] = atr.ib[0][ATR_INTERFACE_BYTE_TA].value; DEBUG_COMM2("Set speed to %d bauds", card_baudrate); } else { DEBUG_COMM2("Reader does not support %d bauds", card_baudrate); /* TA2 present -> specific mode: the card is supporting * only the baud rate specified in TA1 but reader does not * support this value. Reject the card. */ if (atr.ib[1][ATR_INTERFACE_BYTE_TA].present) return IFD_COMMUNICATION_ERROR; } } else { /* the card is too fast for the reader */ if ((card_baudrate > ccid_desc->dwMaxDataRate +2) /* but TA1 <= 97 */ && (atr.ib[0][ATR_INTERFACE_BYTE_TA].value <= 0x97) /* and the reader has a baud rate table */ && ccid_desc->arrayOfSupportedDataRates) { unsigned char old_TA1; old_TA1 = atr.ib[0][ATR_INTERFACE_BYTE_TA].value; while (atr.ib[0][ATR_INTERFACE_BYTE_TA].value > 0x94) { /* use a lower TA1 */ atr.ib[0][ATR_INTERFACE_BYTE_TA].value--; ATR_GetParameter(&atr, ATR_PARAMETER_D, &d); ATR_GetParameter(&atr, ATR_PARAMETER_F, &f); /* Baudrate = f x D/F */ card_baudrate = (unsigned int) (1000 * ccid_desc->dwDefaultClock * d / f); if (find_baud_rate(card_baudrate, ccid_desc->arrayOfSupportedDataRates)) { pps[1] |= 0x10; /* PTS1 presence */ pps[2] = atr.ib[0][ATR_INTERFACE_BYTE_TA].value; DEBUG_COMM2("Set adapted speed to %d bauds", card_baudrate); break; } } /* restore original TA1 value */ atr.ib[0][ATR_INTERFACE_BYTE_TA].value = old_TA1; } } } } /* PTS2? */ if (Flags & IFD_NEGOTIATE_PTS2) { pps[1] |= 0x20; /* PTS2 presence */ pps[3] = PTS2; } /* PTS3? */ if (Flags & IFD_NEGOTIATE_PTS3) { pps[1] |= 0x40; /* PTS3 presence */ pps[4] = PTS3; } /* Generate PPS */ pps[0] = 0xFF; /* Automatic PPS made by the ICC? */ if ((! (ccid_desc->dwFeatures & CCID_CLASS_AUTO_PPS_CUR)) /* TA2 absent: negociable mode */ && (! atr.ib[1][ATR_INTERFACE_BYTE_TA].present)) { int default_protocol; if (ATR_MALFORMED == ATR_GetDefaultProtocol(&atr, &default_protocol)) return IFD_PROTOCOL_NOT_SUPPORTED; /* if the requested protocol is not the default one * or a TA1/PPS1 is present */ if (((pps[1] & 0x0F) != default_protocol) || (PPS_HAS_PPS1(pps))) if (PPS_Exchange(reader_index, pps, &len, &pps[2]) != PPS_OK) { DEBUG_INFO("PPS_Exchange Failed"); return IFD_ERROR_PTS_FAILURE; } } /* Now we must set the reader parameters */ ATR_GetConvention(&atr, &convention); /* specific mode and implicit parameters? (b5 of TA2) */ if (atr.ib[1][ATR_INTERFACE_BYTE_TA].present && (atr.ib[1][ATR_INTERFACE_BYTE_TA].value & 0x10)) return IFD_COMMUNICATION_ERROR; /* T=1 */ if (SCARD_PROTOCOL_T1 == Protocol) { BYTE param[] = { 0x11, /* Fi/Di */ 0x10, /* TCCKS */ 0x00, /* GuardTime */ 0x4D, /* BWI/CWI */ 0x00, /* ClockStop */ 0x20, /* IFSC */ 0x00 /* NADValue */ }; int i; t1_state_t *t1 = &(ccid_slot -> t1); RESPONSECODE ret; double f, d; /* TA1 is not default */ if (PPS_HAS_PPS1(pps)) param[0] = pps[2]; /* CRC checksum? */ if (2 == t1->rc_bytes) param[1] |= 0x01; /* the CCID should ignore this bit */ if (ATR_CONVENTION_INVERSE == convention) param[1] |= 0x02; /* get TC1 Extra guard time */ if (atr.ib[0][ATR_INTERFACE_BYTE_TC].present) param[2] = atr.ib[0][ATR_INTERFACE_BYTE_TC].value; /* TBi (i>2) present? BWI/CWI */ for (i=2; i2) must be used */ break; } /* compute communication timeout */ ATR_GetParameter(&atr, ATR_PARAMETER_F, &f); ATR_GetParameter(&atr, ATR_PARAMETER_D, &d); ccid_desc->readTimeout = T1_card_timeout(f, d, param[2], (param[3] & 0xF0) >> 4 /* BWI */, param[3] & 0x0F /* CWI */, ccid_desc->dwDefaultClock); /* TAi (i>2) present? IFSC */ for (i=2; i2) must be used */ break; } DEBUG_COMM2("Timeout: %d seconds", ccid_desc->readTimeout); ret = SetParameters(reader_index, 1, sizeof(param), param); if (IFD_SUCCESS != ret) return ret; } else /* T=0 */ { BYTE param[] = { 0x11, /* Fi/Di */ 0x00, /* TCCKS */ 0x00, /* GuardTime */ 0x0A, /* WaitingInteger */ 0x00 /* ClockStop */ }; RESPONSECODE ret; double f, d; /* TA1 is not default */ if (PPS_HAS_PPS1(pps)) param[0] = pps[2]; if (ATR_CONVENTION_INVERSE == convention) param[1] |= 0x02; /* get TC1 Extra guard time */ if (atr.ib[0][ATR_INTERFACE_BYTE_TC].present) param[2] = atr.ib[0][ATR_INTERFACE_BYTE_TC].value; /* TC2 WWT */ if (atr.ib[1][ATR_INTERFACE_BYTE_TC].present) param[3] = atr.ib[1][ATR_INTERFACE_BYTE_TC].value; /* compute communication timeout */ ATR_GetParameter(&atr, ATR_PARAMETER_F, &f); ATR_GetParameter(&atr, ATR_PARAMETER_D, &d); ccid_desc->readTimeout = T0_card_timeout(f, d, param[2] /* TC1 */, param[3] /* TC2 */, ccid_desc->dwDefaultClock); DEBUG_COMM2("Communication timeout: %d seconds", ccid_desc->readTimeout); ret = SetParameters(reader_index, 0, sizeof(param), param); if (IFD_SUCCESS != ret) return ret; } /* set IFSC & IFSD in T=1 */ if (SCARD_PROTOCOL_T1 == Protocol) { t1_state_t *t1 = &(ccid_slot -> t1); int i; /* TAi (i>2) present? */ for (i=2; i2) must be used */ break; } /* IFSD not negociated by the reader? */ if (! (ccid_desc->dwFeatures & CCID_CLASS_AUTO_IFSD)) { DEBUG_COMM2("Negociate IFSD at %d", ccid_desc -> dwMaxIFSD); if (t1_negociate_ifsd(t1, 0, ccid_desc -> dwMaxIFSD) < 0) return IFD_COMMUNICATION_ERROR; } t1_set_param(t1, IFD_PROTOCOL_T1_IFSD, ccid_desc -> dwMaxIFSD); DEBUG_COMM3("T=1: IFSC=%d, IFSD=%d", t1->ifsc, t1->ifsd); } /* store used protocol for use by the secure commands (verify/change PIN) */ ccid_desc->cardProtocol = Protocol; return IFD_SUCCESS; } /* IFDHSetProtocolParameters */ EXTERNAL RESPONSECODE IFDHPowerICC(DWORD Lun, DWORD Action, PUCHAR Atr, PDWORD AtrLength) { /* * This function controls the power and reset signals of the smartcard * reader at the particular reader/slot specified by Lun. * * Action - Action to be taken on the card. * * IFD_POWER_UP - Power and reset the card if not done so (store the * ATR and return it and it's length). * * IFD_POWER_DOWN - Power down the card if not done already * (Atr/AtrLength should be zero'd) * * IFD_RESET - Perform a quick reset on the card. If the card is not * powered power up the card. (Store and return the Atr/Length) * * Atr - Answer to Reset of the card. The driver is responsible for * caching this value in case IFDHGetCapabilities is called requesting * the ATR and it's length. This should not exceed MAX_ATR_SIZE. * * AtrLength - Length of the Atr. This should not exceed * MAX_ATR_SIZE. * * Notes: * * Memory cards without an ATR should return IFD_SUCCESS on reset but * the Atr should be zero'd and the length should be zero * * Reset errors should return zero for the AtrLength and return * IFD_ERROR_POWER_ACTION. * * returns: * * IFD_SUCCESS IFD_ERROR_POWER_ACTION IFD_COMMUNICATION_ERROR * IFD_NOT_SUPPORTED */ unsigned int nlength; RESPONSECODE return_value = IFD_SUCCESS; unsigned char pcbuffer[RESP_BUF_SIZE]; int reader_index; const char *actions[] = { "PowerUp", "PowerDown", "Reset" }; DEBUG_INFO3("lun: %X, action: %s", Lun, actions[Action-IFD_POWER_UP]); /* By default, assume it won't work :) */ *AtrLength = 0; if (-1 == (reader_index = LunToReaderIndex(Lun))) return IFD_COMMUNICATION_ERROR; switch (Action) { case IFD_POWER_DOWN: /* Clear ATR buffer */ CcidSlots[reader_index].nATRLength = 0; *CcidSlots[reader_index].pcATRBuffer = '\0'; /* Memorise the request */ CcidSlots[reader_index].bPowerFlags |= MASK_POWERFLAGS_PDWN; /* send the command */ if (IFD_SUCCESS != CmdPowerOff(reader_index)) { DEBUG_CRITICAL("PowerDown failed"); return_value = IFD_ERROR_POWER_ACTION; goto end; } /* clear T=1 context */ t1_release(&(get_ccid_slot(reader_index) -> t1)); break; case IFD_POWER_UP: case IFD_RESET: nlength = sizeof(pcbuffer); if (CmdPowerOn(reader_index, &nlength, pcbuffer, PowerOnVoltage) != IFD_SUCCESS) { DEBUG_CRITICAL("PowerUp failed"); return_value = IFD_ERROR_POWER_ACTION; goto end; } /* Power up successful, set state variable to memorise it */ CcidSlots[reader_index].bPowerFlags |= MASK_POWERFLAGS_PUP; CcidSlots[reader_index].bPowerFlags &= ~MASK_POWERFLAGS_PDWN; /* Reset is returned, even if TCK is wrong */ CcidSlots[reader_index].nATRLength = *AtrLength = (nlength < MAX_ATR_SIZE) ? nlength : MAX_ATR_SIZE; memcpy(Atr, pcbuffer, *AtrLength); memcpy(CcidSlots[reader_index].pcATRBuffer, pcbuffer, *AtrLength); /* initialise T=1 context */ t1_init(&(get_ccid_slot(reader_index) -> t1), reader_index); break; default: DEBUG_CRITICAL("Action not supported"); return_value = IFD_NOT_SUPPORTED; } end: return return_value; } /* IFDHPowerICC */ EXTERNAL RESPONSECODE IFDHTransmitToICC(DWORD Lun, SCARD_IO_HEADER SendPci, PUCHAR TxBuffer, DWORD TxLength, PUCHAR RxBuffer, PDWORD RxLength, /*@unused@*/ PSCARD_IO_HEADER RecvPci) { /* * This function performs an APDU exchange with the card/slot * specified by Lun. The driver is responsible for performing any * protocol specific exchanges such as T=0/1 ... differences. Calling * this function will abstract all protocol differences. * * SendPci Protocol - 0, 1, .... 14 Length - Not used. * * TxBuffer - Transmit APDU example (0x00 0xA4 0x00 0x00 0x02 0x3F * 0x00) TxLength - Length of this buffer. RxBuffer - Receive APDU * example (0x61 0x14) RxLength - Length of the received APDU. This * function will be passed the size of the buffer of RxBuffer and this * function is responsible for setting this to the length of the * received APDU. This should be ZERO on all errors. The resource * manager will take responsibility of zeroing out any temporary APDU * buffers for security reasons. * * RecvPci Protocol - 0, 1, .... 14 Length - Not used. * * Notes: The driver is responsible for knowing what type of card it * has. If the current slot/card contains a memory card then this * command should ignore the Protocol and use the MCT style commands * for support for these style cards and transmit them appropriately. * If your reader does not support memory cards or you don't want to * then ignore this. * * RxLength should be set to zero on error. * * returns: * * IFD_SUCCESS IFD_COMMUNICATION_ERROR IFD_RESPONSE_TIMEOUT * IFD_ICC_NOT_PRESENT IFD_PROTOCOL_NOT_SUPPORTED */ RESPONSECODE return_value; unsigned int rx_length; int reader_index; DEBUG_INFO2("lun: %X", Lun); if (-1 == (reader_index = LunToReaderIndex(Lun))) return IFD_COMMUNICATION_ERROR; rx_length = *RxLength; return_value = CmdXfrBlock(reader_index, TxLength, TxBuffer, &rx_length, RxBuffer, SendPci.Protocol); if (IFD_SUCCESS == return_value) *RxLength = rx_length; else *RxLength = 0; return return_value; } /* IFDHTransmitToICC */ EXTERNAL RESPONSECODE IFDHControl(DWORD Lun, DWORD dwControlCode, PUCHAR TxBuffer, DWORD TxLength, PUCHAR RxBuffer, DWORD RxLength, PDWORD pdwBytesReturned) { /* * This function performs a data exchange with the reader (not the * card) specified by Lun. Here XXXX will only be used. It is * responsible for abstracting functionality such as PIN pads, * biometrics, LCD panels, etc. You should follow the MCT, CTBCS * specifications for a list of accepted commands to implement. * * TxBuffer - Transmit data TxLength - Length of this buffer. RxBuffer * - Receive data RxLength - Length of the received data. This * function will be passed the length of the buffer RxBuffer and it * must set this to the length of the received data. * * Notes: RxLength should be zero on error. */ RESPONSECODE return_value = IFD_COMMUNICATION_ERROR; int reader_index; DEBUG_INFO3("lun: %X, ControlCode: 0x%X", Lun, dwControlCode); DEBUG_INFO_XXD("Control TxBuffer: ", TxBuffer, TxLength); reader_index = LunToReaderIndex(Lun); if ((-1 == reader_index) || (NULL == pdwBytesReturned)) return return_value; /* Set the return length to 0 to avoid problems */ *pdwBytesReturned = 0; if (IOCTL_SMARTCARD_VENDOR_IFD_EXCHANGE == dwControlCode) { if (FALSE == (DriverOptions & DRIVER_OPTION_CCID_EXCHANGE_AUTHORIZED)) { DEBUG_INFO("ifd exchange (Escape command) not allowed"); return_value = IFD_COMMUNICATION_ERROR; } else { unsigned int iBytesReturned; iBytesReturned = RxLength; return_value = CmdEscape(reader_index, TxBuffer, TxLength, RxBuffer, &iBytesReturned); *pdwBytesReturned = iBytesReturned; } } /* Implement the PC/SC v2.1.2 Part 10 IOCTL mechanism */ /* Query for features */ if (CM_IOCTL_GET_FEATURE_REQUEST == dwControlCode) { unsigned int iBytesReturned = 0; PCSC_TLV_STRUCTURE *pcsc_tlv = (PCSC_TLV_STRUCTURE *)RxBuffer; /* we need room for two records */ if (RxLength < 2 * sizeof(PCSC_TLV_STRUCTURE)) return IFD_COMMUNICATION_ERROR; /* We can only support direct verify and/or modify currently */ if (get_ccid_descriptor(reader_index) -> bPINSupport & CCID_CLASS_PIN_VERIFY) { pcsc_tlv -> tag = FEATURE_VERIFY_PIN_DIRECT; pcsc_tlv -> length = 0x04; /* always 0x04 */ pcsc_tlv -> value = htonl(IOCTL_FEATURE_VERIFY_PIN_DIRECT); pcsc_tlv++; iBytesReturned += sizeof(PCSC_TLV_STRUCTURE); } if (get_ccid_descriptor(reader_index) -> bPINSupport & CCID_CLASS_PIN_MODIFY) { pcsc_tlv -> tag = FEATURE_MODIFY_PIN_DIRECT; pcsc_tlv -> length = 0x04; /* always 0x04 */ pcsc_tlv -> value = htonl(IOCTL_FEATURE_MODIFY_PIN_DIRECT); pcsc_tlv++; iBytesReturned += sizeof(PCSC_TLV_STRUCTURE); } *pdwBytesReturned = iBytesReturned; return_value = IFD_SUCCESS; } /* Verify a PIN, plain CCID */ if (IOCTL_FEATURE_VERIFY_PIN_DIRECT == dwControlCode) { unsigned int iBytesReturned; iBytesReturned = RxLength; return_value = SecurePINVerify(reader_index, TxBuffer, TxLength, RxBuffer, &iBytesReturned); *pdwBytesReturned = iBytesReturned; } /* Modify a PIN, plain CCID */ if (IOCTL_FEATURE_MODIFY_PIN_DIRECT == dwControlCode) { unsigned int iBytesReturned; iBytesReturned = RxLength; return_value = SecurePINModify(reader_index, TxBuffer, TxLength, RxBuffer, &iBytesReturned); *pdwBytesReturned = iBytesReturned; } if (IFD_SUCCESS != return_value) *pdwBytesReturned = 0; DEBUG_INFO_XXD("Control RxBuffer: ", RxBuffer, *pdwBytesReturned); return return_value; } /* IFDHControl */ EXTERNAL RESPONSECODE IFDHICCPresence(DWORD Lun) { /* * This function returns the status of the card inserted in the * reader/slot specified by Lun. It will return either: * * returns: IFD_ICC_PRESENT IFD_ICC_NOT_PRESENT * IFD_COMMUNICATION_ERROR */ unsigned char pcbuffer[SIZE_GET_SLOT_STATUS]; RESPONSECODE return_value = IFD_COMMUNICATION_ERROR; int oldLogLevel; int reader_index; _ccid_descriptor *ccid_descriptor; unsigned int oldReadTimeout; DEBUG_PERIODIC2("lun: %X", Lun); if (-1 == (reader_index = LunToReaderIndex(Lun))) return IFD_COMMUNICATION_ERROR; ccid_descriptor = get_ccid_descriptor(reader_index); /* save the current read timeout computed from card capabilities */ oldReadTimeout = ccid_descriptor->readTimeout; /* use default timeout since the reader may not be present anymore */ ccid_descriptor->readTimeout = DEFAULT_COM_READ_TIMEOUT; /* if DEBUG_LEVEL_PERIODIC is not set we remove DEBUG_LEVEL_COMM */ oldLogLevel = LogLevel; if (! (LogLevel & DEBUG_LEVEL_PERIODIC)) LogLevel &= ~DEBUG_LEVEL_COMM; return_value = CmdGetSlotStatus(reader_index, pcbuffer); /* set back the old timeout */ ccid_descriptor->readTimeout = oldReadTimeout; /* set back the old LogLevel */ LogLevel = oldLogLevel; if (return_value != IFD_SUCCESS) return return_value; return_value = IFD_COMMUNICATION_ERROR; switch (pcbuffer[7] & CCID_ICC_STATUS_MASK) /* bStatus */ { case CCID_ICC_PRESENT_ACTIVE: return_value = IFD_ICC_PRESENT; /* use default slot */ break; case CCID_ICC_PRESENT_INACTIVE: if ((CcidSlots[reader_index].bPowerFlags == POWERFLAGS_RAZ) || (CcidSlots[reader_index].bPowerFlags & MASK_POWERFLAGS_PDWN)) /* the card was previously absent */ return_value = IFD_ICC_PRESENT; else { /* the card was previously present but has been * removed and inserted between two consecutive * IFDHICCPresence() calls */ CcidSlots[reader_index].bPowerFlags = POWERFLAGS_RAZ; return_value = IFD_ICC_NOT_PRESENT; } break; case CCID_ICC_ABSENT: /* Reset ATR buffer */ CcidSlots[reader_index].nATRLength = 0; *CcidSlots[reader_index].pcATRBuffer = '\0'; /* Reset PowerFlags */ CcidSlots[reader_index].bPowerFlags = POWERFLAGS_RAZ; return_value = IFD_ICC_NOT_PRESENT; break; } /* SCR331-DI contactless reader */ if (((SCR331DI == ccid_descriptor->readerID) || (SDI010 == ccid_descriptor->readerID) || (SCR331DINTTCOM == ccid_descriptor->readerID)) && (ccid_descriptor->bCurrentSlotIndex > 0)) { unsigned char cmd[] = { 0x11 }; /* command: 11 ?? * response: 00 11 01 ?? no card * 01 04 00 ?? card present */ unsigned char res[10]; unsigned int length_res = sizeof(res); RESPONSECODE ret; /* if DEBUG_LEVEL_PERIODIC is not set we remove DEBUG_LEVEL_COMM */ oldLogLevel = LogLevel; if (! (LogLevel & DEBUG_LEVEL_PERIODIC)) LogLevel &= ~DEBUG_LEVEL_COMM; ret = CmdEscape(reader_index, cmd, sizeof(cmd), res, &length_res); /* set back the old LogLevel */ LogLevel = oldLogLevel; if (ret != IFD_SUCCESS) { DEBUG_INFO("CmdEscape failed"); /* simulate a card absent */ res[0] = 0; } if (0x01 == res[0]) return_value = IFD_ICC_PRESENT; else { /* Reset ATR buffer */ CcidSlots[reader_index].nATRLength = 0; *CcidSlots[reader_index].pcATRBuffer = '\0'; /* Reset PowerFlags */ CcidSlots[reader_index].bPowerFlags = POWERFLAGS_RAZ; return_value = IFD_ICC_NOT_PRESENT; } } DEBUG_PERIODIC2("Card %s", IFD_ICC_PRESENT == return_value ? "present" : "absent"); return return_value; } /* IFDHICCPresence */ CcidDesc *get_ccid_slot(unsigned int reader_index) { return &CcidSlots[reader_index]; } /* get_ccid_slot */ void init_driver(void) { char keyValue[TOKEN_MAX_VALUE_SIZE]; char infofile[FILENAME_MAX]; /* Info.plist full patch filename */ snprintf(infofile, sizeof(infofile), "%s/%s/Contents/Info.plist", PCSCLITE_HP_DROPDIR, BUNDLE); /* Log level */ if (0 == LTPBundleFindValueWithKey(infofile, "ifdLogLevel", keyValue, 0)) { /* convert from hex or dec or octal */ LogLevel = strtoul(keyValue, NULL, 0); /* print the log level used */ DEBUG_INFO2("LogLevel: 0x%.4X", LogLevel); } /* Driver options */ if (0 == LTPBundleFindValueWithKey(infofile, "ifdDriverOptions", keyValue, 0)) { /* convert from hex or dec or octal */ DriverOptions = strtoul(keyValue, NULL, 0); /* print the log level used */ DEBUG_INFO2("DriverOptions: 0x%.4X", DriverOptions); } /* get the voltage parameter */ switch ((DriverOptions >> 4) & 0x03) { case 0: PowerOnVoltage = VOLTAGE_5V; break; case 1: PowerOnVoltage = VOLTAGE_3V; break; case 2: PowerOnVoltage = VOLTAGE_1_8V; break; case 3: PowerOnVoltage = VOLTAGE_AUTO; break; } /* initialise the Lun to reader_index mapping */ InitReaderIndex(); DebugInitialized = TRUE; } /* init_driver */ void extra_egt(ATR_t *atr, _ccid_descriptor *ccid_desc, DWORD Protocol) { /* This function use an EGT value for cards who comply with followings * criterias: * - TA1 > 11 * - current EGT = 0x00 or 0xFF * - T=0 or (T=1 and CWI >= 2) * * Without this larger EGT some non ISO 7816-3 smart cards may not * communicate with the reader. * * This modification is harmless, the reader will just be less restrictive */ unsigned int card_baudrate; unsigned int default_baudrate; double f, d; int i; /* if TA1 not present */ if (! atr->ib[0][ATR_INTERFACE_BYTE_TA].present) return; ATR_GetParameter(atr, ATR_PARAMETER_D, &d); ATR_GetParameter(atr, ATR_PARAMETER_F, &f); /* may happen with non ISO cards */ if ((0 == f) || (0 == d)) return; /* Baudrate = f x D/F */ card_baudrate = (unsigned int) (1000 * ccid_desc->dwDefaultClock * d / f); default_baudrate = (unsigned int) (1000 * ccid_desc->dwDefaultClock * ATR_DEFAULT_D / ATR_DEFAULT_F); /* TA1 > 11? */ if (card_baudrate <= default_baudrate) return; /* Current EGT = 0 or FF? */ if (atr->ib[0][ATR_INTERFACE_BYTE_TC].present && ((0x00 == atr->ib[0][ATR_INTERFACE_BYTE_TC].value) || (0xFF == atr->ib[0][ATR_INTERFACE_BYTE_TC].value))) { if (SCARD_PROTOCOL_T0 == Protocol) { /* Init TC1 */ atr->ib[0][ATR_INTERFACE_BYTE_TC].present = TRUE; atr->ib[0][ATR_INTERFACE_BYTE_TC].value = 2; DEBUG_INFO("Extra EGT patch applied"); } if (SCARD_PROTOCOL_T1 == Protocol) { /* TBi (i>2) present? BWI/CWI */ for (i=2; i= 2 ? */ if (atr->ib[i][ATR_INTERFACE_BYTE_TB].present && ((atr->ib[i][ATR_INTERFACE_BYTE_TB].value & 0x0F) >= 2)) { /* Init TC1 */ atr->ib[0][ATR_INTERFACE_BYTE_TC].present = TRUE; atr->ib[0][ATR_INTERFACE_BYTE_TC].value = 2; DEBUG_INFO("Extra EGT patch applied"); /* only the first TBi (i>2) must be used */ break; } } } } } /* extra_egt */ static char find_baud_rate(unsigned int baudrate, unsigned int *list) { int i; DEBUG_COMM2("Card baud rate: %d", baudrate); /* Does the reader support the announced smart card data speed? */ for (i=0;; i++) { /* end of array marker */ if (0 == list[i]) break; DEBUG_COMM2("Reader can do: %d", list[i]); /* We must take into account that the card_baudrate integral value * is an approximative result, computed from the d/f float result. */ if ((baudrate < list[i] + 2) && (baudrate > list[i] - 2)) return TRUE; } return FALSE; } /* find_baud_rate */ static unsigned int T0_card_timeout(double f, double d, int TC1, int TC2, int clock_frequency) { unsigned int timeout = DEFAULT_COM_READ_TIMEOUT; double EGT, WWT; unsigned int t; /* Timeout applied on ISO_IN or ISO_OUT card exchange * we choose the maximum computed value. * * ISO_IN timeout is the sum of: * Terminal: Smart card: * 5 bytes header cmd -> * <- Procedure byte * 256 data bytes -> * <- SW1-SW2 * = 261 EGT + 3 WWT + 3 WWT * * ISO_OUT Timeout is the sum of: * Terminal: Smart card: * 5 bytes header cmd -> * <- Procedure byte + 256 data bytes + SW1-SW2 * = 5 EGT + 1 WWT + 259 WWT */ /* clock_frequency is in kHz so the times are in milliseconds and not * in seconds */ /* may happen with non ISO cards */ if ((0 == f) || (0 == d) || (0 == clock_frequency)) return 60; /* 60 seconds */ /* EGT */ /* see ch. 6.5.3 Extra Guard Time, page 12 of ISO 7816-3 */ EGT = 12 * f / d / clock_frequency + (f / d) * TC1 / clock_frequency; /* card WWT */ /* see ch. 8.2 Character level, page 15 of ISO 7816-3 */ WWT = 960 * TC2 * f / clock_frequency; /* ISO in */ t = 261 * EGT + (3 + 3) * WWT; /* Convert from milliseonds to seconds rouned to the upper value * use +1 instead of ceil() to round up to the nearest interger * so we can avoid a dependency on the math library */ t = t/1000 +1; if (timeout < t) timeout = t; /* ISO out */ t = 5 * EGT + (1 + 259) * WWT; t = t/1000 +1; if (timeout < t) timeout = t; return timeout; } /* T0_card_timeout */ static unsigned int T1_card_timeout(double f, double d, int TC1, int BWI, int CWI, int clock_frequency) { double EGT, BWT, CWT, etu; unsigned int timeout; /* Timeout applied on ISO in + ISO out card exchange * * Timeout is the sum of: * - ISO in delay between leading edge of the first character sent by the * interface device and the last one (NAD PCB LN APDU CKS) = 260 EGT, * - delay between ISO in and ISO out = BWT, * - ISO out delay between leading edge of the first character sent by the * card and the last one (NAD PCB LN DATAS CKS) = 260 CWT. */ /* clock_frequency is in kHz so the times are in milliseconds and not * in seconds */ /* may happen with non ISO cards */ if ((0 == f) || (0 == d) || (0 == clock_frequency)) return 60; /* 60 seconds */ /* see ch. 6.5.2 Transmission factors F and D, page 12 of ISO 7816-3 */ etu = f / d / clock_frequency; /* EGT */ /* see ch. 6.5.3 Extra Guard Time, page 12 of ISO 7816-3 */ EGT = 12 * etu + (f / d) * TC1 / clock_frequency; /* card BWT */ /* see ch. 9.5.3.2 Block Waiting Time, page 20 of ISO 7816-3 */ BWT = 11 * etu + (1<