/***************************************************************************** * * CloseSerial: close the port * *****************************************************************************/ status_t CloseSerial(unsigned int reader_index) { unsigned int reader = reader_index; /* device not opened */ if (NULL == serialDevice[reader_index].device) return STATUS_UNSUCCESSFUL; DEBUG_COMM2("Closing serial device: %s", serialDevice[reader_index].device); /* Decrement number of opened slot */ (*serialDevice[reader_index].nb_opened_slots)--; /* release the allocated ressources for the last slot only */ if (0 == *serialDevice[reader_index].nb_opened_slots) { DEBUG_COMM("Last slot closed. Release resources"); (void)close(serialDevice[reader].fd); serialDevice[reader].fd = -1; free(serialDevice[reader].device); serialDevice[reader].device = NULL; } return STATUS_SUCCESS; } /* CloseSerial */
/***************************************************************************** * * CloseUSB * ****************************************************************************/ status_t CloseUSB(unsigned int reader_index) { /* device not opened */ if (usbDevice[reader_index].handle == NULL) return STATUS_UNSUCCESSFUL; DEBUG_COMM3("Closing USB device: %s/%s", usbDevice[reader_index].dirname, usbDevice[reader_index].filename); if (usbDevice[reader_index].ccid.arrayOfSupportedDataRates && (usbDevice[reader_index].ccid.bCurrentSlotIndex == 0)) { free(usbDevice[reader_index].ccid.arrayOfSupportedDataRates); usbDevice[reader_index].ccid.arrayOfSupportedDataRates = NULL; } /* one slot closed */ (*usbDevice[reader_index].nb_opened_slots)--; /* release the allocated ressources for the last slot only */ if (0 == *usbDevice[reader_index].nb_opened_slots) { DEBUG_COMM("Last slot closed. Release resources"); /* reset so that bSeq starts at 0 again */ if (DriverOptions & DRIVER_OPTION_RESET_ON_CLOSE) (void)usb_reset(usbDevice[reader_index].handle); (void)usb_release_interface(usbDevice[reader_index].handle, usbDevice[reader_index].interface); (void)usb_close(usbDevice[reader_index].handle); #ifdef __APPLE__ DEBUG_INFO3("Terminating thread: %s/%s", usbDevice[reader_index].dirname, usbDevice[reader_index].filename); // Terminate thread *usbDevice[reader_index].pTerminated = TRUE; pthread_join(usbDevice[reader_index].hThread, NULL); // Free bStatus lock pthread_mutex_destroy(usbDevice[reader_index].ccid.pbStatusLock); #endif // Free array of bStatus free(usbDevice[reader_index].ccid.bStatus); free(usbDevice[reader_index].dirname); free(usbDevice[reader_index].filename); } /* mark the resource unused */ usbDevice[reader_index].handle = NULL; usbDevice[reader_index].dirname = NULL; usbDevice[reader_index].filename = NULL; usbDevice[reader_index].interface = 0; usbDevice[reader_index].ccid.bStatus = NULL; // Array of bStatus return STATUS_SUCCESS; } /* CloseUSB */
/***************************************************************************** * * CloseUSB * ****************************************************************************/ status_t CloseUSB(unsigned int reader_index) { /* device not opened */ if (usbDevice[reader_index].dev_handle == NULL) return STATUS_UNSUCCESSFUL; DEBUG_COMM3("Closing USB device: %d/%d", usbDevice[reader_index].bus_number, usbDevice[reader_index].device_address); if (usbDevice[reader_index].ccid.arrayOfSupportedDataRates && (usbDevice[reader_index].ccid.bCurrentSlotIndex == 0)) { free(usbDevice[reader_index].ccid.arrayOfSupportedDataRates); usbDevice[reader_index].ccid.arrayOfSupportedDataRates = NULL; } if (usbDevice[reader_index].ccid.gemalto_firmware_features) { free(usbDevice[reader_index].ccid.gemalto_firmware_features); usbDevice[reader_index].ccid.gemalto_firmware_features = NULL ; } /* one slot closed */ (*usbDevice[reader_index].nb_opened_slots)--; /* release the allocated ressources for the last slot only */ if (0 == *usbDevice[reader_index].nb_opened_slots) { DEBUG_COMM("Last slot closed. Release resources"); if (usbDevice[reader_index].ccid.sIFD_serial_number) free(usbDevice[reader_index].ccid.sIFD_serial_number); if (usbDevice[reader_index].ccid.sIFD_iManufacturer) free(usbDevice[reader_index].ccid.sIFD_iManufacturer); /* reset so that bSeq starts at 0 again */ if (DriverOptions & DRIVER_OPTION_RESET_ON_CLOSE) (void)libusb_reset_device(usbDevice[reader_index].dev_handle); (void)libusb_release_interface(usbDevice[reader_index].dev_handle, usbDevice[reader_index].interface); (void)libusb_close(usbDevice[reader_index].dev_handle); /* does not work for libusb <= 1.0.8 */ /* libusb_exit(ctx); */ } /* mark the resource unused */ usbDevice[reader_index].dev_handle = NULL; usbDevice[reader_index].interface = 0; return STATUS_SUCCESS; } /* CloseUSB */
/***************************************************************************** * * get_bytes: get n bytes * *****************************************************************************/ int get_bytes(unsigned int reader_index, unsigned char *buffer, int length) { int offset = serialDevice[reader_index].buffer_offset; int offset_last = serialDevice[reader_index].buffer_offset_last; DEBUG_COMM3("available: %d, needed: %d", offset_last-offset, length); /* enough data are available */ if (offset + length <= offset_last) { DEBUG_COMM("data available"); memcpy(buffer, serialDevice[reader_index].buffer + offset, length); serialDevice[reader_index].buffer_offset += length; } else { int present, rv; /* copy available data */ present = offset_last - offset; if (present > 0) { DEBUG_COMM2("some data available: %d", present); memcpy(buffer, serialDevice[reader_index].buffer + offset, present); } /* get fresh data */ DEBUG_COMM2("get more data: %d", length - present); rv = ReadChunk(reader_index, serialDevice[reader_index].buffer, sizeof(serialDevice[reader_index].buffer), length - present); if (rv < 0) return STATUS_COMM_ERROR; /* fill the buffer */ memcpy(buffer + present, serialDevice[reader_index].buffer, length - present); serialDevice[reader_index].buffer_offset = length - present; serialDevice[reader_index].buffer_offset_last = rv; DEBUG_COMM3("offset: %d, last_offset: %d", serialDevice[reader_index].buffer_offset, serialDevice[reader_index].buffer_offset_last); } return STATUS_SUCCESS; } /* get_bytes */
/***************************************************************************** * * CloseUSB * ****************************************************************************/ status_t CloseUSB(unsigned int reader_index) { /* device not opened */ if (usbDevice[reader_index].handle == NULL) return STATUS_UNSUCCESSFUL; DEBUG_COMM3("Closing USB device: %s/%s", usbDevice[reader_index].dirname, usbDevice[reader_index].filename); if (usbDevice[reader_index].ccid.arrayOfSupportedDataRates && (usbDevice[reader_index].ccid.bCurrentSlotIndex == 0)) { free(usbDevice[reader_index].ccid.arrayOfSupportedDataRates); usbDevice[reader_index].ccid.arrayOfSupportedDataRates = NULL; } /* one slot closed */ (*usbDevice[reader_index].nb_opened_slots)--; /* release the allocated ressources for the last slot only */ if (0 == *usbDevice[reader_index].nb_opened_slots) { DEBUG_COMM("Last slot closed. Release resources"); /* reset so that bSeq starts at 0 again */ if (DriverOptions & DRIVER_OPTION_RESET_ON_CLOSE) (void)usb_reset(usbDevice[reader_index].handle); (void)usb_release_interface(usbDevice[reader_index].handle, usbDevice[reader_index].interface); (void)usb_close(usbDevice[reader_index].handle); free(usbDevice[reader_index].dirname); free(usbDevice[reader_index].filename); } /* mark the resource unused */ usbDevice[reader_index].handle = NULL; usbDevice[reader_index].dirname = NULL; usbDevice[reader_index].filename = NULL; usbDevice[reader_index].interface = 0; return STATUS_SUCCESS; } /* CloseUSB */
/***************************************************************************** * * ccid_open_hack_pre * ****************************************************************************/ int ccid_open_hack_pre(unsigned int reader_index) { _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index); switch (ccid_descriptor->readerID) { case MYSMARTPAD: ccid_descriptor->dwMaxIFSD = 254; break; case CL1356D: /* the firmware needs some time to initialize */ (void)sleep(1); ccid_descriptor->readTimeout = 60*1000; /* 60 seconds */ break; case OZ776: case OZ776_7772: ccid_descriptor->dwMaxDataRate = 9600; break; case ElatecTWN4: /* use a timeout of 400 ms instead of 100 ms in CmdGetSlotStatus() * used by CreateChannelByNameOrChannel() * The reader answers after 280 ms if no tag is present */ case SCM_SCL011: /* The SCM SCL011 reader needs 350 ms to answer */ ccid_descriptor->readTimeout = DEFAULT_COM_READ_TIMEOUT * 4; break; } /* CCID */ if ((PROTOCOL_CCID == ccid_descriptor->bInterfaceProtocol) && (3 == ccid_descriptor -> bNumEndpoints)) { #ifndef TWIN_SERIAL /* just wait for 100ms in case a notification is in the pipe */ (void)InterruptRead(reader_index, 100); #endif } /* ICCD type A */ if (PROTOCOL_ICCD_A == ccid_descriptor->bInterfaceProtocol) { unsigned char tmp[MAX_ATR_SIZE]; unsigned int n = sizeof(tmp); DEBUG_COMM("ICCD type A"); (void)CmdPowerOff(reader_index); (void)CmdPowerOn(reader_index, &n, tmp, CCID_CLASS_AUTO_VOLTAGE); (void)CmdPowerOff(reader_index); } /* ICCD type B */ if (PROTOCOL_ICCD_B == ccid_descriptor->bInterfaceProtocol) { unsigned char tmp[MAX_ATR_SIZE]; unsigned int n = sizeof(tmp); DEBUG_COMM("ICCD type B"); if (CCID_CLASS_SHORT_APDU == (ccid_descriptor->dwFeatures & CCID_CLASS_EXCHANGE_MASK)) { /* use the extended APDU comm alogorithm */ ccid_descriptor->dwFeatures &= ~CCID_CLASS_EXCHANGE_MASK; ccid_descriptor->dwFeatures |= CCID_CLASS_EXTENDED_APDU; } (void)CmdPowerOff(reader_index); (void)CmdPowerOn(reader_index, &n, tmp, CCID_CLASS_AUTO_VOLTAGE); (void)CmdPowerOff(reader_index); } return 0; } /* ccid_open_hack_pre */
/***************************************************************************** * * ccid_open_hack_post * ****************************************************************************/ int ccid_open_hack_post(unsigned int reader_index) { _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index); RESPONSECODE return_value = IFD_SUCCESS; switch (ccid_descriptor->readerID) { case GEMPCKEY: case GEMPCTWIN: /* Reader announces TPDU but can do APDU (EMV in fact) */ if (DriverOptions & DRIVER_OPTION_GEMPC_TWIN_KEY_APDU) { unsigned char cmd[] = { 0x1F, 0x02 }; unsigned char res[10]; unsigned int length_res = sizeof(res); if (CmdEscape(reader_index, cmd, sizeof(cmd), res, &length_res, 0) == IFD_SUCCESS) { ccid_descriptor->dwFeatures &= ~CCID_CLASS_EXCHANGE_MASK; ccid_descriptor->dwFeatures |= CCID_CLASS_SHORT_APDU; } } break; case VEGAALPHA: case GEMPCPINPAD: /* load the l10n strings in the pinpad memory */ { #define L10N_HEADER_SIZE 5 #define L10N_STRING_MAX_SIZE 16 #define L10N_NB_STRING 10 unsigned char cmd[L10N_HEADER_SIZE + L10N_NB_STRING * L10N_STRING_MAX_SIZE]; unsigned char res[20]; unsigned int length_res = sizeof(res); int offset, i, j; const char *fr[] = { "Entrer PIN", "Nouveau PIN", "Confirmer PIN", "PIN correct", "PIN Incorrect !", "Delai depasse", "* essai restant", "Inserer carte", "Erreur carte", "PIN bloque" }; const char *de[] = { "PIN eingeben", "Neue PIN", "PIN bestatigen", "PIN korrect", "Falsche PIN !", "Zeit abgelaufen", "* Versuche ubrig", "Karte einstecken", "Fehler Karte", "PIN blockiert" }; const char *es[] = { "Introducir PIN", "Nuevo PIN", "Confirmar PIN", "PIN OK", "PIN Incorrecto !", "Tiempo Agotado", "* ensayos quedan", "Introducir Tarj.", "Error en Tarjeta", "PIN bloqueado" }; const char *it[] = { "Inserire PIN", "Nuovo PIN", "Confermare PIN", "PIN Corretto", "PIN Errato !", "Tempo scaduto", "* prove rimaste", "Inserire Carta", "Errore Carta", "PIN ostruito"}; const char *pt[] = { "Insira PIN", "Novo PIN", "Conf. novo PIN", "PIN OK", "PIN falhou!", "Tempo expirou", "* tentiv. restam", "Introduza cartao", "Erro cartao", "PIN bloqueado" }; const char *nl[] = { "Inbrengen code", "Nieuwe code", "Bevestig code", "Code aanvaard", "Foute code", "Time out", "* Nog Pogingen", "Kaart inbrengen", "Kaart fout", "Kaart blok" }; const char *tr[] = { "PIN Giriniz", "Yeni PIN", "PIN Onayala", "PIN OK", "Yanlis PIN", "Zaman Asimi", "* deneme kaldi", "Karti Takiniz", "Kart Hatasi", "Kart Kilitli" }; const char *en[] = { "Enter PIN", "New PIN", "Confirm PIN", "PIN OK", "Incorrect PIN!", "Time Out", "* retries left", "Insert Card", "Card Error", "PIN blocked" }; const char *lang; const char **l10n; #ifdef __APPLE__ CFArrayRef cfa; CFStringRef slang; /* Get the complete ordered list */ cfa = CFLocaleCopyPreferredLanguages(); /* Use the first/preferred language * As the driver is run as root we get the language * selected during install */ slang = CFArrayGetValueAtIndex(cfa, 0); /* CFString -> C string */ lang = CFStringGetCStringPtr(slang, kCFStringEncodingMacRoman); #else /* The other Unixes just use the LANG env variable */ lang = getenv("LANG"); #endif DEBUG_COMM2("Using lang: %s", lang); if (NULL == lang) l10n = en; else { if (0 == strncmp(lang, "fr", 2)) l10n = fr; else if (0 == strncmp(lang, "de", 2)) l10n = de; else if (0 == strncmp(lang, "es", 2)) l10n = es; else if (0 == strncmp(lang, "it", 2)) l10n = it; else if (0 == strncmp(lang, "pt", 2)) l10n = pt; else if (0 == strncmp(lang, "nl", 2)) l10n = nl; else if (0 == strncmp(lang, "tr", 2)) l10n = tr; else l10n = en; } #ifdef __APPLE__ /* Release the allocated array */ CFRelease(cfa); #endif offset = 0; cmd[offset++] = 0xB2; /* load strings */ cmd[offset++] = 0xA0; /* address of the memory */ cmd[offset++] = 0x00; /* address of the first byte */ cmd[offset++] = 0x4D; /* magic value */ cmd[offset++] = 0x4C; /* magic value */ /* for each string */ for (i=0; i<L10N_NB_STRING; i++) { /* copy the string */ for (j=0; l10n[i][j]; j++) cmd[offset++] = l10n[i][j]; /* pad with " " */ for (; j<L10N_STRING_MAX_SIZE; j++) cmd[offset++] = ' '; } (void)sleep(1); if (IFD_SUCCESS == CmdEscape(reader_index, cmd, sizeof(cmd), res, &length_res, DEFAULT_COM_READ_TIMEOUT)) { DEBUG_COMM("l10n string loaded successfully"); } else { DEBUG_COMM("Failed to load l10n strings"); return_value = IFD_COMMUNICATION_ERROR; } if (DriverOptions & DRIVER_OPTION_DISABLE_PIN_RETRIES) { /* disable VERIFY from reader */ const unsigned char cmd2[] = {0xb5, 0x00}; length_res = sizeof(res); if (IFD_SUCCESS == CmdEscape(reader_index, cmd2, sizeof(cmd2), res, &length_res, DEFAULT_COM_READ_TIMEOUT)) { DEBUG_COMM("Disable SPE retry counter successful"); } else { DEBUG_CRITICAL("Failed to disable SPE retry counter"); } } } break; case HPSMARTCARDKEYBOARD: case HP_CCIDSMARTCARDKEYBOARD: case FUJITSUSMARTKEYB: /* the Secure Pin Entry is bogus so disable it * https://web.archive.org/web/20120320001756/http://martinpaljak.net/2011/03/19/insecure-hp-usb-smart-card-keyboard/ * * The problem is that the PIN code entered using the Secure * Pin Entry function is also sent to the host. */ ccid_descriptor->bPINSupport = 0; break; case HID_AVIATOR: /* The chip advertises pinpad but actually doesn't have one */ ccid_descriptor->bPINSupport = 0; /* Firmware uses chaining */ ccid_descriptor->dwFeatures &= ~CCID_CLASS_EXCHANGE_MASK; ccid_descriptor->dwFeatures |= CCID_CLASS_EXTENDED_APDU; break; #if 0 /* SCM SCR331-DI contactless */ case SCR331DI: /* SCM SCR331-DI-NTTCOM contactless */ case SCR331DINTTCOM: /* SCM SDI010 contactless */ case SDI010: /* the contactless reader is in the second slot */ if (ccid_descriptor->bCurrentSlotIndex > 0) { unsigned char cmd1[] = { 0x00 }; /* command: 00 ?? * response: 06 10 03 03 00 00 00 01 FE FF FF FE 01 ?? */ unsigned char cmd2[] = { 0x02 }; /* command: 02 ?? * response: 00 ?? */ unsigned char res[20]; unsigned int length_res = sizeof(res); if ((IFD_SUCCESS == CmdEscape(reader_index, cmd1, sizeof(cmd1), res, &length_res, 0)) && (IFD_SUCCESS == CmdEscape(reader_index, cmd2, sizeof(cmd2), res, &length_res, 0))) { DEBUG_COMM("SCM SCR331-DI contactless detected"); } else { DEBUG_COMM("SCM SCR331-DI contactless init failed"); } /* hack since the contactless reader do not share dwFeatures */ ccid_descriptor->dwFeatures &= ~CCID_CLASS_EXCHANGE_MASK; ccid_descriptor->dwFeatures |= CCID_CLASS_SHORT_APDU; ccid_descriptor->dwFeatures |= CCID_CLASS_AUTO_IFSD; } break; #endif case CHERRY_KC1000SC: if ((0x0100 == ccid_descriptor->IFD_bcdDevice) && (ccid_descriptor->dwFeatures & CCID_CLASS_EXCHANGE_MASK) == CCID_CLASS_SHORT_APDU) { /* firmware 1.00 is bogus * With a T=1 card and case 2 APDU (data from card to * host) the maximum size returned by the reader is 128 * byes. The reader is then using chaining as with * extended APDU. */ ccid_descriptor->dwFeatures &= ~CCID_CLASS_EXCHANGE_MASK; ccid_descriptor->dwFeatures |= CCID_CLASS_EXTENDED_APDU; } break; case ElatecTWN4: case SCM_SCL011: /* restore default timeout (modified in ccid_open_hack_pre()) */ ccid_descriptor->readTimeout = DEFAULT_COM_READ_TIMEOUT; break; } /* Gemalto readers may report additional information */ if (GET_VENDOR(ccid_descriptor->readerID) == VENDOR_GEMALTO) set_gemalto_firmware_features(reader_index); return return_value; } /* ccid_open_hack_post */
/***************************************************************************** * * ccid_open_hack_pre * ****************************************************************************/ int ccid_open_hack_pre(unsigned int reader_index) { _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index); switch (ccid_descriptor->readerID) { case MYSMARTPAD: ccid_descriptor->dwMaxIFSD = 254; break; case CL1356D: /* the firmware needs some time to initialize */ (void)sleep(1); ccid_descriptor->readTimeout = 60*1000; /* 60 seconds */ break; case GEMPCTWIN: case GEMPCKEY: case DELLSCRK: /* Only the chipset with firmware version 2.00 is "bogus" * The reader may send packets of 0 bytes when the reader is * connected to a USB 3 port */ if (0x0200 == ccid_descriptor->IFD_bcdDevice) { ccid_descriptor->zlp = TRUE; DEBUG_INFO1("ZLP fixup"); } break; case OZ776_7772: ccid_descriptor->dwMaxDataRate = 9600; break; case SCR331NTTCOM: ccid_descriptor->bInterfaceProtocol = 0; break; } /* CCID */ if ((PROTOCOL_CCID == ccid_descriptor->bInterfaceProtocol) && (3 == ccid_descriptor -> bNumEndpoints)) { #ifndef TWIN_SERIAL /* just wait for 100ms in case a notification is in the pipe */ (void)InterruptRead(reader_index, 100); #endif } /* ICCD type A */ if (PROTOCOL_ICCD_A == ccid_descriptor->bInterfaceProtocol) { unsigned char tmp[MAX_ATR_SIZE]; unsigned int n = sizeof(tmp); DEBUG_COMM("ICCD type A"); (void)CmdPowerOff(reader_index); (void)CmdPowerOn(reader_index, &n, tmp, CCID_CLASS_AUTO_VOLTAGE); (void)CmdPowerOff(reader_index); } /* ICCD type B */ if (PROTOCOL_ICCD_B == ccid_descriptor->bInterfaceProtocol) { unsigned char tmp[MAX_ATR_SIZE]; unsigned int n = sizeof(tmp); DEBUG_COMM("ICCD type B"); if (CCID_CLASS_SHORT_APDU == (ccid_descriptor->dwFeatures & CCID_CLASS_EXCHANGE_MASK)) { /* use the extended APDU comm alogorithm */ ccid_descriptor->dwFeatures &= ~CCID_CLASS_EXCHANGE_MASK; ccid_descriptor->dwFeatures |= CCID_CLASS_EXTENDED_APDU; } (void)CmdPowerOff(reader_index); (void)CmdPowerOn(reader_index, &n, tmp, CCID_CLASS_AUTO_VOLTAGE); (void)CmdPowerOff(reader_index); } return 0; } /* ccid_open_hack_pre */
/***************************************************************************** * * CloseUSB * ****************************************************************************/ status_t CloseUSB(unsigned int reader_index) { /* device not opened */ if (usbDevice[reader_index].dev_handle == NULL) return STATUS_UNSUCCESSFUL; DEBUG_COMM3("Closing USB device: %d/%d", usbDevice[reader_index].bus_number, usbDevice[reader_index].device_address); /* one slot closed */ (*usbDevice[reader_index].nb_opened_slots)--; /* release the allocated ressources for the last slot only */ if (0 == *usbDevice[reader_index].nb_opened_slots) { struct usbDevice_MultiSlot_Extension *msExt; DEBUG_COMM("Last slot closed. Release resources"); msExt = usbDevice[reader_index].multislot_extension; /* If this is a multislot reader, close using the multislot stuff */ if (msExt) { /* terminate the interrupt waiter thread */ Multi_PollingTerminate(msExt); /* wait for the thread to actually terminate */ pthread_join(msExt->thread_proc, NULL); /* release the shared objects */ pthread_cond_destroy(&msExt->condition); pthread_mutex_destroy(&msExt->mutex); /* Deallocate the extension itself */ free(msExt); /* Stop the slot */ usbDevice[reader_index].multislot_extension = NULL; } if (usbDevice[reader_index].ccid.gemalto_firmware_features) free(usbDevice[reader_index].ccid.gemalto_firmware_features); if (usbDevice[reader_index].ccid.sIFD_serial_number) free(usbDevice[reader_index].ccid.sIFD_serial_number); if (usbDevice[reader_index].ccid.sIFD_iManufacturer) free(usbDevice[reader_index].ccid.sIFD_iManufacturer); if (usbDevice[reader_index].ccid.arrayOfSupportedDataRates) free(usbDevice[reader_index].ccid.arrayOfSupportedDataRates); (void)libusb_release_interface(usbDevice[reader_index].dev_handle, usbDevice[reader_index].interface); (void)libusb_close(usbDevice[reader_index].dev_handle); } /* mark the resource unused */ usbDevice[reader_index].dev_handle = NULL; usbDevice[reader_index].interface = 0; close_libusb_if_needed(); return STATUS_SUCCESS; } /* CloseUSB */
/***************************************************************************** * * OpenSerialByName: open the port * *****************************************************************************/ status_t OpenSerialByName(unsigned int reader_index, char *dev_name) { struct termios current_termios; unsigned int reader = reader_index; /* 255 is MAX_DEVICENAME in pcscd.h */ char reader_name[255] = "GemPCTwin"; char *p; status_t ret; DEBUG_COMM3("Reader index: %X, Device: %s", reader_index, dev_name); /* parse dev_name using the pattern "device:name" */ p = strchr(dev_name, ':'); if (p) { /* copy the second part of the string */ strlcpy(reader_name, p+1, sizeof(reader_name)); /* replace ':' by '\0' so that dev_name only contains the device name */ *p = '\0'; } ret = set_ccid_descriptor(reader_index, reader_name, dev_name); if (STATUS_UNSUCCESSFUL == ret) return STATUS_UNSUCCESSFUL; /* secondary slot so do not physically open the device */ if (STATUS_SECONDARY_SLOT == ret) return STATUS_SUCCESS; serialDevice[reader].fd = open(dev_name, O_RDWR | O_NOCTTY); if (-1 == serialDevice[reader].fd) { DEBUG_CRITICAL3("open %s: %s", dev_name, strerror(errno)); return STATUS_UNSUCCESSFUL; } /* Set RTS signal to low to prevent the smart card reader * from sending its plug and play string. */ { int flags; if (ioctl(serialDevice[reader].fd, TIOCMGET, &flags) < 0) { DEBUG_CRITICAL2("Get RS232 signals state failed: %s", strerror(errno)); } else { flags &= ~TIOCM_RTS; if (ioctl(serialDevice[reader].fd, TIOCMSET, &flags) < 0) { DEBUG_CRITICAL2("Set RTS to low failed: %s", strerror(errno)); } else { DEBUG_COMM("Plug-n-Play inhibition successful"); } } } /* set channel used */ serialDevice[reader].device = strdup(dev_name); /* empty in and out serial buffers */ if (tcflush(serialDevice[reader].fd, TCIOFLUSH)) DEBUG_INFO2("tcflush() function error: %s", strerror(errno)); /* get config attributes */ if (tcgetattr(serialDevice[reader].fd, ¤t_termios) == -1) { DEBUG_INFO2("tcgetattr() function error: %s", strerror(errno)); (void)close(serialDevice[reader].fd); serialDevice[reader].fd = -1; return STATUS_UNSUCCESSFUL; } /* IGNBRK: ignore BREAK condition on input * IGNPAR: ignore framing errors and parity errors. */ current_termios.c_iflag = IGNBRK | IGNPAR; current_termios.c_oflag = 0; /* Raw output modes */ /* CS8: 8-bits character size * CSTOPB: set two stop bits * CREAD: enable receiver * CLOCAL: ignore modem control lines */ current_termios.c_cflag = CS8 | CSTOPB | CREAD | CLOCAL; /* Do not echo characters because if you connect to a host it or your modem * will echo characters for you. Don't generate signals. */ current_termios.c_lflag = 0; #ifndef ANDROID /* set serial port speed to 115200 bauds */ (void)cfsetspeed(¤t_termios, B115200); DEBUG_INFO("Set serial port baudrate to 115200 and correct configuration"); #endif if (tcsetattr(serialDevice[reader].fd, TCSANOW, ¤t_termios) == -1) { (void)close(serialDevice[reader].fd); serialDevice[reader].fd = -1; DEBUG_INFO2("tcsetattr error: %s", strerror(errno)); return STATUS_UNSUCCESSFUL; } /* perform a command to be sure a Gemalto reader is connected * get the reader firmware */ { unsigned char tx_buffer[] = { 0x02 }; unsigned char rx_buffer[50]; unsigned int rx_length = sizeof(rx_buffer); /* 2 seconds timeout to not wait too long if no reader is connected */ serialDevice[reader].ccid.readTimeout = 2*1000; if (IFD_SUCCESS != CmdEscape(reader_index, tx_buffer, sizeof(tx_buffer), rx_buffer, &rx_length)) { DEBUG_CRITICAL("Get firmware failed. Maybe the reader is not connected"); (void)CloseSerial(reader_index); return STATUS_UNSUCCESSFUL; } /* normal timeout: 2 seconds */ serialDevice[reader].ccid.readTimeout = DEFAULT_COM_READ_TIMEOUT ; rx_buffer[rx_length] = '\0'; DEBUG_INFO2("Firmware: %s", rx_buffer); } /* perform a command to configure GemPC Twin reader card movement * notification to synchronous mode: the card movement is notified _after_ * the host command and _before_ the reader anwser */ { unsigned char tx_buffer[] = { 0x01, 0x01, 0x01}; unsigned char rx_buffer[50]; unsigned int rx_length = sizeof(rx_buffer); if (IFD_SUCCESS != CmdEscape(reader_index, tx_buffer, sizeof(tx_buffer), rx_buffer, &rx_length)) { DEBUG_CRITICAL("Change card movement notification failed."); (void)CloseSerial(reader_index); return STATUS_UNSUCCESSFUL; } } serialDevice[reader_index].ccid.sIFD_serial_number = NULL; serialDevice[reader_index].ccid.sIFD_iManufacturer = NULL; serialDevice[reader_index].ccid.IFD_bcdDevice = 0; return STATUS_SUCCESS; } /* OpenSerialByName */
/***************************************************************************** * * ReadSerial: Receive bytes from the card reader * *****************************************************************************/ status_t ReadSerial(unsigned int reader_index, unsigned int *length, unsigned char *buffer) { unsigned char c; int rv; int echo; int to_read; int i; /* we get the echo first */ echo = serialDevice[reader_index].echo; start: DEBUG_COMM("start"); if ((rv = get_bytes(reader_index, &c, 1)) != STATUS_SUCCESS) return rv; if (c == RDR_to_PC_NotifySlotChange) goto slot_change; if (c == SYNC) goto sync; if (c >= 0x80) { DEBUG_COMM2("time request: 0x%02X", c); goto start; } DEBUG_CRITICAL2("Got 0x%02X", c); return STATUS_COMM_ERROR; slot_change: DEBUG_COMM("slot change"); if ((rv = get_bytes(reader_index, &c, 1)) != STATUS_SUCCESS) return rv; if (c == CARD_ABSENT) { DEBUG_COMM("Card removed"); } else if (c == CARD_PRESENT) { DEBUG_COMM("Card inserted"); } else { DEBUG_COMM2("Unknown card movement: %d", c); } goto start; sync: DEBUG_COMM("sync"); if ((rv = get_bytes(reader_index, &c, 1)) != STATUS_SUCCESS) return rv; if (c == CTRL_ACK) goto ack; if (c == CTRL_NAK) goto nak; DEBUG_CRITICAL2("Got 0x%02X instead of ACK/NAK", c); return STATUS_COMM_ERROR; nak: DEBUG_COMM("nak"); if ((rv = get_bytes(reader_index, &c, 1)) != STATUS_SUCCESS) return rv; if (c != (SYNC ^ CTRL_NAK)) { DEBUG_CRITICAL2("Wrong LRC: 0x%02X", c); return STATUS_COMM_ERROR; } else { DEBUG_COMM("NAK requested"); return STATUS_COMM_NAK; } ack: DEBUG_COMM("ack"); /* normal CCID frame */ if ((rv = get_bytes(reader_index, buffer, 5)) != STATUS_SUCCESS) return rv; /* total frame size */ to_read = 10+dw2i(buffer, 1); if ((to_read < 10) || (to_read > (int)*length)) { DEBUG_CRITICAL2("Wrong value for frame size: %d", to_read); return STATUS_COMM_ERROR; } DEBUG_COMM2("frame size: %d", to_read); if ((rv = get_bytes(reader_index, buffer+5, to_read-5)) != STATUS_SUCCESS) return rv; DEBUG_XXD("frame: ", buffer, to_read); /* lrc */ DEBUG_COMM("lrc"); if ((rv = get_bytes(reader_index, &c, 1)) != STATUS_SUCCESS) return rv; DEBUG_COMM2("lrc: 0x%02X", c); for (i=0; i<to_read; i++) c ^= buffer[i]; if (c != (SYNC ^ CTRL_ACK)) DEBUG_CRITICAL2("Wrong LRC: 0x%02X", c); if (echo) { echo = FALSE; goto start; } /* length of data read */ *length = to_read; return STATUS_SUCCESS; } /* ReadSerial */
/* * Send an APDU through T=1 */ int t1_transceive(t1_state_t * t1, unsigned int dad, const void *snd_buf, size_t snd_len, void *rcv_buf, size_t rcv_len) { ct_buf_t sbuf, rbuf, tbuf; unsigned char sdata[T1_BUFFER_SIZE], sblk[5]; unsigned int slen, retries, resyncs, sent_length = 0; size_t last_send = 0; if (snd_len == 0) return -1; /* we can't talk to a dead card / reader. Reset it! */ if (t1->state == DEAD) { DEBUG_CRITICAL("T=1 state machine is DEAD. Reset the card first."); return -1; } t1->state = SENDING; retries = t1->retries; resyncs = 3; /* Initialize send/recv buffer */ ct_buf_set(&sbuf, (void *)snd_buf, snd_len); ct_buf_init(&rbuf, rcv_buf, rcv_len); /* Send the first block */ slen = t1_build(t1, sdata, dad, T1_I_BLOCK, &sbuf, &last_send); while (1) { unsigned char pcb; int n; retries--; n = t1_xcv(t1, sdata, slen, sizeof(sdata)); if (-2 == n) { DEBUG_COMM("Parity error"); /* ISO 7816-3 Rule 7.4.2 */ if (retries == 0) goto resync; /* ISO 7816-3 Rule 7.2 */ if (T1_R_BLOCK == t1_block_type(t1->previous_block[PCB])) { DEBUG_COMM("Rule 7.2"); slen = t1_rebuild(t1, sdata); continue; } slen = t1_build(t1, sdata, dad, T1_R_BLOCK | T1_EDC_ERROR, NULL, NULL); continue; } if (n < 0) { DEBUG_CRITICAL("fatal: transmit/receive failed"); t1->state = DEAD; goto error; } if ((sdata[NAD] != swap_nibbles(dad)) /* wrong NAD */ || (sdata[LEN] == 0xFF)) /* length == 0xFF (illegal) */ { DEBUG_COMM("R-BLOCK required"); /* ISO 7816-3 Rule 7.4.2 */ if (retries == 0) goto resync; /* ISO 7816-3 Rule 7.2 */ if (T1_R_BLOCK == t1_block_type(t1->previous_block[PCB])) { DEBUG_COMM("Rule 7.2"); slen = t1_rebuild(t1, sdata); continue; } slen = t1_build(t1, sdata, dad, T1_R_BLOCK | T1_OTHER_ERROR, NULL, NULL); continue; } if (!t1_verify_checksum(t1, sdata, n)) { DEBUG_COMM("checksum failed"); /* ISO 7816-3 Rule 7.4.2 */ if (retries == 0) goto resync; /* ISO 7816-3 Rule 7.2 */ if (T1_R_BLOCK == t1_block_type(t1->previous_block[PCB])) { DEBUG_COMM("Rule 7.2"); slen = t1_rebuild(t1, sdata); continue; } slen = t1_build(t1, sdata, dad, T1_R_BLOCK | T1_EDC_ERROR, NULL, NULL); continue; } pcb = sdata[PCB]; switch (t1_block_type(pcb)) { case T1_R_BLOCK: if ((sdata[LEN] != 0x00) /* length != 0x00 (illegal) */ || (pcb & 0x20) /* b6 of pcb is set */ ) { DEBUG_COMM("R-Block required"); /* ISO 7816-3 Rule 7.4.2 */ if (retries == 0) goto resync; /* ISO 7816-3 Rule 7.2 */ if (T1_R_BLOCK == t1_block_type(t1->previous_block[1])) { DEBUG_COMM("Rule 7.2"); slen = t1_rebuild(t1, sdata); continue; } slen = t1_build(t1, sdata, dad, T1_R_BLOCK | T1_OTHER_ERROR, NULL, NULL); continue; } if (((t1_seq(pcb) != t1->ns) /* wrong sequence number & no bit more */ && ! t1->more) ) { DEBUG_COMM4("received: %d, expected: %d, more: %d", t1_seq(pcb), t1->ns, t1->more); /* ISO 7816-3 Rule 7.2 */ if (T1_R_BLOCK == t1_block_type(t1->previous_block[PCB])) { DEBUG_COMM("Rule 7.2"); slen = t1_rebuild(t1, sdata); continue; } DEBUG_COMM("R-Block required"); /* ISO 7816-3 Rule 7.4.2 */ if (retries == 0) goto resync; slen = t1_build(t1, sdata, dad, T1_R_BLOCK | T1_OTHER_ERROR, NULL, NULL); continue; } if (t1->state == RECEIVING) { /* ISO 7816-3 Rule 7.2 */ if (T1_R_BLOCK == t1_block_type(t1->previous_block[1])) { DEBUG_COMM("Rule 7.2"); slen = t1_rebuild(t1, sdata); continue; } DEBUG_COMM(""); slen = t1_build(t1, sdata, dad, T1_R_BLOCK, NULL, NULL); break; } /* If the card terminal requests the next * sequence number, it received the previous * block successfully */ if (t1_seq(pcb) != t1->ns) { ct_buf_get(&sbuf, NULL, last_send); sent_length += last_send; last_send = 0; t1->ns ^= 1; } /* If there's no data available, the ICC * shouldn't be asking for more */ if (ct_buf_avail(&sbuf) == 0) goto resync; slen = t1_build(t1, sdata, dad, T1_I_BLOCK, &sbuf, &last_send); break; case T1_I_BLOCK: /* The first I-block sent by the ICC indicates * the last block we sent was received successfully. */ if (t1->state == SENDING) { DEBUG_COMM(""); ct_buf_get(&sbuf, NULL, last_send); last_send = 0; t1->ns ^= 1; } t1->state = RECEIVING; /* If the block sent by the card doesn't match * what we expected it to send, reply with * an R block */ if (t1_seq(pcb) != t1->nr) { DEBUG_COMM("wrong nr"); slen = t1_build(t1, sdata, dad, T1_R_BLOCK | T1_OTHER_ERROR, NULL, NULL); continue; } t1->nr ^= 1; if (ct_buf_put(&rbuf, sdata + 3, sdata[LEN]) < 0) { DEBUG_CRITICAL2("buffer overrun by %d bytes", sdata[LEN] - (rbuf.size - rbuf.tail)); goto error; } if ((pcb & T1_MORE_BLOCKS) == 0) goto done; slen = t1_build(t1, sdata, dad, T1_R_BLOCK, NULL, NULL); break; case T1_S_BLOCK: if (T1_S_IS_RESPONSE(pcb) && t1->state == RESYNCH) { /* ISO 7816-3 Rule 6.2 */ DEBUG_COMM("S-Block answer received"); /* ISO 7816-3 Rule 6.3 */ t1->state = SENDING; sent_length = 0; last_send = 0; resyncs = 3; retries = t1->retries; ct_buf_init(&rbuf, rcv_buf, rcv_len); slen = t1_build(t1, sdata, dad, T1_I_BLOCK, &sbuf, &last_send); continue; } if (T1_S_IS_RESPONSE(pcb)) { /* ISO 7816-3 Rule 7.4.2 */ if (retries == 0) goto resync; /* ISO 7816-3 Rule 7.2 */ if (T1_R_BLOCK == t1_block_type(t1->previous_block[PCB])) { DEBUG_COMM("Rule 7.2"); slen = t1_rebuild(t1, sdata); continue; } DEBUG_CRITICAL("wrong response S-BLOCK received"); slen = t1_build(t1, sdata, dad, T1_R_BLOCK | T1_OTHER_ERROR, NULL, NULL); continue; } ct_buf_init(&tbuf, sblk, sizeof(sblk)); DEBUG_COMM("S-Block request received"); switch (T1_S_TYPE(pcb)) { case T1_S_RESYNC: if (sdata[LEN] != 0) { DEBUG_COMM2("Wrong length: %d", sdata[LEN]); slen = t1_build(t1, sdata, dad, T1_R_BLOCK | T1_OTHER_ERROR, NULL, NULL); continue; } DEBUG_COMM("Resync requested"); /* the card is not allowed to send a resync. */ goto resync; case T1_S_ABORT: if (sdata[LEN] != 0) { DEBUG_COMM2("Wrong length: %d", sdata[LEN]); slen = t1_build(t1, sdata, dad, T1_R_BLOCK | T1_OTHER_ERROR, NULL, NULL); continue; } /* ISO 7816-3 Rule 9 */ DEBUG_CRITICAL("abort requested"); break; case T1_S_IFS: if (sdata[LEN] != 1) { DEBUG_COMM2("Wrong length: %d", sdata[LEN]); slen = t1_build(t1, sdata, dad, T1_R_BLOCK | T1_OTHER_ERROR, NULL, NULL); continue; } DEBUG_CRITICAL2("CT sent S-block with ifs=%u", sdata[DATA]); if (sdata[DATA] == 0) goto resync; t1->ifsc = sdata[DATA]; ct_buf_putc(&tbuf, sdata[DATA]); break; case T1_S_WTX: if (sdata[LEN] != 1) { DEBUG_COMM2("Wrong length: %d", sdata[LEN]); slen = t1_build(t1, sdata, dad, T1_R_BLOCK | T1_OTHER_ERROR, NULL, NULL); continue; } DEBUG_COMM2("CT sent S-block with wtx=%u", sdata[DATA]); t1->wtx = sdata[DATA]; ct_buf_putc(&tbuf, sdata[DATA]); break; default: DEBUG_CRITICAL2("T=1: Unknown S block type 0x%02x", T1_S_TYPE(pcb)); goto resync; } slen = t1_build(t1, sdata, dad, T1_S_BLOCK | T1_S_RESPONSE | T1_S_TYPE(pcb), &tbuf, NULL); } /* Everything went just splendid */ retries = t1->retries; continue; resync: /* the number or resyncs is limited, too */ /* ISO 7816-3 Rule 6.4 */ if (resyncs == 0) goto error; /* ISO 7816-3 Rule 6 */ resyncs--; t1->ns = 0; t1->nr = 0; slen = t1_build(t1, sdata, dad, T1_S_BLOCK | T1_S_RESYNC, NULL, NULL); t1->state = RESYNCH; t1->more = FALSE; retries = 1; continue; } done: return ct_buf_avail(&rbuf); error: t1->state = DEAD; return -1; }