/* cardterminal.c Card Terminal handling and CT-BCS functions This file is part of the Unix driver for Towitoko smartcard readers Copyright (C) 2000 Carlos Prados 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 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 */ #include "defines.h" #include "cardterminal.h" #include "atr.h" #include "atr_sync.h" #include #include /* * Not exported constants definition */ #define CARDTERMINAL_RESETCT_BUFFER_SIZE 35 #define CARDTERMINAL_REQUESTICC_BUFFER_SIZE 35 #define CARDTERMINAL_GETSTATUS_BUFFER_SIZE 19 #define CARDTERMINAL_EJECTICC_BUFFER_SIZE 2 #define CARDTERMINAL_MANUFACTURER "DETWK" /* * Not exported functions declaration */ static char CardTerminal_ResetCT (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp); static char CardTerminal_RequestICC (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp); static char CardTerminal_GetStatus (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp); static char CardTerminal_EjectICC (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp); static void CardTerminal_Clear (CardTerminal * ct); /* * Exported functions definition */ CardTerminal * CardTerminal_New (void) { CardTerminal *ct; ct = (CardTerminal *) malloc (sizeof (CardTerminal)); if (ct != NULL) CardTerminal_Clear (ct); return ct; } char CardTerminal_Init (CardTerminal * ct, unsigned short pn) { char ret; int i; bool usbserial; /* Create a new IO_Serial */ ct->io = IO_Serial_New (); /* Memory error */ if (ct->io == NULL) return ERR_MEMORY; /* * Handle USB port numbers: first USB serial port starts at 0x8000, * 0x8001 if CTAPI_WIN32_COM is set */ if ((pn & 0x8000) == 0x8000) { usbserial = TRUE; pn &= 0x7FFF; } else usbserial = FALSE; /* Initialise serial port */ #ifndef CTAPI_WIN32_COM if (!IO_Serial_Init (ct->io, pn + 1, usbserial, TRUE)) #else if (!IO_Serial_Init (ct->io, pn, usbserial, TRUE)) #endif { IO_Serial_Delete (ct->io); ct->io = NULL; return ERR_TRANS; } /* Cearte all reader slots */ ct->num_slots = 0; do { i = ct->num_slots++; /* Create one slot */ ct->slots[i] = CT_Slot_New (); if (ct->slots[i] == NULL) { ret = ERR_MEMORY; break; } /* Initialise slot */ ret = CT_Slot_Init (ct->slots[i], ct->io, i); if (ret != OK) break; } while (!CT_Slot_IsLast(ct->slots[i])); /* On error restore initial state */ if (ret != OK) { while (ct->num_slots > 0) { if (ct->slots[i] != NULL) { CT_Slot_Delete (ct->slots[i]); ct->slots[i] = NULL; } ct->num_slots --; i--; } IO_Serial_Close (ct->io); IO_Serial_Delete (ct->io); ct->io = NULL; } #ifdef HAVE_PTHREAD_H else pthread_mutex_init(&(ct->mutex), NULL); #endif return ret; } char CardTerminal_Command (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp) { BYTE buffer[CTBCS_MIN_RESPONSE_SIZE], cla, ins; long length; char ret; /* Get class of command */ cla = APDU_Cmd_Cla (cmd); if (cla != CTBCS_CLA) { length = CTBCS_MIN_RESPONSE_SIZE; buffer[0] = CTBCS_SW1_WRONG_CLA; buffer[1] = CTBCS_SW2_WRONG_CLA; (*rsp) = APDU_Rsp_New (buffer, length); ret = OK; } else { /* Get instruction */ ins = APDU_Cmd_Ins (cmd); /* Reset CT */ if (ins == CTBCS_INS_RESET) ret = CardTerminal_ResetCT (ct, cmd, rsp); /* Request ICC */ else if (ins == CTBCS_INS_REQUEST) ret = CardTerminal_RequestICC (ct, cmd, rsp); /* Get Status */ else if (ins == CTBCS_INS_STATUS) ret = CardTerminal_GetStatus (ct, cmd, rsp); /* Eject ICC */ else if (ins == CTBCS_INS_EJECT) ret = CardTerminal_EjectICC (ct, cmd, rsp); /* Wrong instruction */ else { length = CTBCS_MIN_RESPONSE_SIZE; buffer[0] = CTBCS_SW1_WRONG_INS; buffer[1] = CTBCS_SW2_WRONG_INS; (*rsp) = APDU_Rsp_New (buffer, length); ret = OK; } } return ret; } char CardTerminal_Close (CardTerminal * ct) { char ret, aux; int i; ret = OK; for (i = 0; i < ct->num_slots; i++) { if (ct->slots[i] != NULL) { aux = CT_Slot_Close (ct->slots[i]); if (aux != OK) ret = aux; CT_Slot_Delete (ct->slots[i]); } } if (ct->io != NULL) { if (!IO_Serial_Close (ct->io)) ret = ERR_TRANS; IO_Serial_Delete (ct->io); } CardTerminal_Clear (ct); #ifdef HAVE_PTHREAD_H pthread_mutex_destroy(&(ct->mutex)); #endif return ret; } void CardTerminal_Delete (CardTerminal * ct) { free (ct); } CT_Slot * CardTerminal_GetSlot (CardTerminal * ct, int number) { if (number < (ct->num_slots)) return ct->slots[number]; return NULL; } #ifdef HAVE_PTHREAD_H pthread_mutex_t * CardTerminal_GetMutex (CardTerminal * ct) { return &(ct->mutex); } #endif /* * Not exported functions definition */ static char CardTerminal_ResetCT (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp) { BYTE buffer[CARDTERMINAL_RESETCT_BUFFER_SIZE], p1, p2; bool card, change; unsigned sn, length; void *atr; char ret = OK; /* Get functional unit */ p1 = APDU_Cmd_P1 (cmd); /* Funcional unit is the card-terminal */ if (p1 == CTBCS_P1_CT_KERNEL) { /* Get command cualifier */ p2 = APDU_Cmd_P2 (cmd); /* Wrong command cualifier */ if (p2 != CTBCS_P2_RESET_NO_RESP && p2 != CTBCS_P2_RESET_GET_ATR && p2 != CTBCS_P2_RESET_GET_HIST) { buffer[0] = CTBCS_SW1_WRONG_PARAM; buffer[1] = CTBCS_SW2_WRONG_PARAM; (*rsp) = APDU_Rsp_New (buffer, 2); return OK; } /* Close slots */ for (sn = 0; sn < ct->num_slots; sn++) { /* Close this slot */ ret = CT_Slot_Close (ct->slots[sn]); if (ret != OK) { buffer[0] = CTBCS_SW1_RESET_ERROR; buffer[1] = CTBCS_SW2_RESET_ERROR; (*rsp) = APDU_Rsp_New (buffer, 2); return ret; } /* Initialise this slot */ ret = CT_Slot_Init (ct->slots[sn],ct->io,sn); if (ret != OK) { buffer[0] = CTBCS_SW1_RESET_ERROR; buffer[1] = CTBCS_SW2_RESET_ERROR; (*rsp) = APDU_Rsp_New (buffer, 2); return ret; } } length = 2; buffer[0] = CTBCS_SW1_RESET_CT_OK; buffer[1] = CTBCS_SW2_RESET_CT_OK; } /* Funtional unit is an ICC */ else if ((p1 == CTBCS_P1_INTERFACE1) || (p1 == CTBCS_P1_INTERFACE2)) { /* Get slot number */ sn = (p1 == CTBCS_P1_INTERFACE1) ? 0 : 1; if (!(sn < ct->num_slots)) { buffer[0] = CTBCS_SW1_WRONG_PARAM; buffer[1] = CTBCS_SW2_WRONG_PARAM; (*rsp) = APDU_Rsp_New (buffer, 2); return ERR_INVALID; } /* Release the slot */ ret = CT_Slot_Release (ct->slots[sn]); if (ret != OK) { buffer[0] = CTBCS_SW1_RESET_ERROR; buffer[1] = CTBCS_SW2_RESET_ERROR; (*rsp) = APDU_Rsp_New (buffer, 2); return ret; } /* Check for card */ ret = CT_Slot_Check (ct->slots[sn], 0, &card, &change); if (ret != OK) { buffer[0] = CTBCS_SW1_RESET_ERROR; buffer[1] = CTBCS_SW2_RESET_ERROR; (*rsp) = APDU_Rsp_New (buffer, 2); return ret; } /* No card present */ if (!card) { buffer[0] = CTBCS_SW1_RESET_ERROR; buffer[1] = CTBCS_SW2_RESET_ERROR;; (*rsp) = APDU_Rsp_New (buffer, 2); return OK; } /* Probe card type */ if (APDU_Cmd_Lc (cmd) > 1) ret = CT_Slot_Probe (ct->slots[sn], APDU_Cmd_Data(cmd), APDU_Cmd_Lc(cmd)); else ret = CT_Slot_Probe (ct->slots[sn], NULL, 0); if (ret != OK || (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_NULL)) { buffer[0] = CTBCS_SW1_RESET_ERROR; buffer[1] = CTBCS_SW2_RESET_ERROR; (*rsp) = APDU_Rsp_New (buffer, 2); return ret; } /* Get command cualifier */ p2 = APDU_Cmd_P2 (cmd); /* Do not return data */ if (p2 == CTBCS_P2_RESET_NO_RESP) { if (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_ICC_ASYNC) { buffer[0] = CTBCS_SW1_RESET_ASYNC_OK; buffer[1] = CTBCS_SW2_RESET_ASYNC_OK; length = 2; } else { buffer[0] = CTBCS_SW1_RESET_SYNC_OK; buffer[1] = CTBCS_SW2_RESET_SYNC_OK; length = 2; } } /* Return complete ATR of ICC */ else if (p2 == CTBCS_P2_RESET_GET_ATR) { atr = CT_Slot_GetAtr (ct->slots[sn]); if (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_ICC_ASYNC) { if (atr != NULL) ATR_GetRaw ((ATR *) atr, buffer, &length); else length = 0; buffer[length] = CTBCS_SW1_RESET_ASYNC_OK; buffer[length + 1] = CTBCS_SW2_RESET_ASYNC_OK; length += 2; } else { if (atr != NULL) ATR_Sync_GetRaw ((ATR_Sync *) atr, buffer, &length); else length = 0; buffer[length] = CTBCS_SW1_RESET_SYNC_OK; buffer[length +1] = CTBCS_SW2_RESET_SYNC_OK; length += 2; } } /* Return historical bytes of ATR */ else if (p2 == CTBCS_P2_RESET_GET_HIST) { atr = CT_Slot_GetAtr (ct->slots[sn]); if (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_ICC_ASYNC) { if (atr != NULL) ATR_GetHistoricalBytes ((ATR *) atr, buffer, &length); else length = 0; buffer[length] = CTBCS_SW1_RESET_ASYNC_OK; buffer[length + 1] = CTBCS_SW2_RESET_ASYNC_OK; length += 2; } else { if (atr != NULL) ATR_Sync_GetHistoricalBytes ((ATR_Sync *) atr, buffer, &length); else length = 0; buffer[length] = CTBCS_SW1_RESET_SYNC_OK; buffer[length + 1] = CTBCS_SW2_RESET_SYNC_OK; length +=2; } } /* Wrong command cualifier */ else { length = 2; buffer[0] = CTBCS_SW1_WRONG_PARAM; buffer[1] = CTBCS_SW2_WRONG_PARAM; ret = OK; } } /* Wrong functional unit */ else { length = 2; buffer[0] = CTBCS_SW1_WRONG_PARAM; buffer[1] = CTBCS_SW2_WRONG_PARAM; ret = OK; } (*rsp) = APDU_Rsp_New (buffer, length); return ret; } static char CardTerminal_RequestICC (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp) { BYTE buffer[CARDTERMINAL_REQUESTICC_BUFFER_SIZE], p1, p2; unsigned timeout, sn, length; bool card, change; void * atr; char ret; /* Get functional unit */ p1 = APDU_Cmd_P1 (cmd); if ((p1 == CTBCS_P1_INTERFACE1) || (p1 == CTBCS_P1_INTERFACE2)) { /* Get the slot number */ sn = (p1 == CTBCS_P1_INTERFACE1) ? 0 : 1; if (CT_Slot_GetICCType (ct->slots[sn]) != CT_SLOT_NULL) { buffer[0] = CTBCS_SW1_REQUEST_CARD_PRESENT; buffer[1] = CTBCS_SW2_REQUEST_CARD_PRESENT; (*rsp) = APDU_Rsp_New (buffer, 2); return OK; } /* Get the card insertion timeout */ if (APDU_Cmd_Lc (cmd) == 1) timeout = (APDU_Cmd_Data (cmd)[0]); else timeout = 0; /* Check for card */ ret = CT_Slot_Check (ct->slots[sn], timeout, &card, &change); if (ret != OK) { buffer[0] = CTBCS_SW1_REQUEST_ERROR; buffer[1] = CTBCS_SW2_REQUEST_ERROR; (*rsp) = APDU_Rsp_New (buffer, 2); return ret; } /* No card present */ if (!card) { buffer[0] = CTBCS_SW1_REQUEST_NO_CARD; buffer[1] = CTBCS_SW2_REQUEST_NO_CARD; (*rsp) = APDU_Rsp_New (buffer, 2); return OK; } /* Probe card type */ if (APDU_Cmd_Lc (cmd) > 1) ret = CT_Slot_Probe (ct->slots[sn], APDU_Cmd_Data(cmd), APDU_Cmd_Lc(cmd)); else ret = CT_Slot_Probe (ct->slots[sn], NULL, 0); if (ret != OK || (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_NULL)) { buffer[0] = CTBCS_SW1_REQUEST_ERROR; buffer[1] = CTBCS_SW2_REQUEST_ERROR; (*rsp) = APDU_Rsp_New (buffer, 2); return ret; } /* Get command cualifier */ p2 = APDU_Cmd_P2 (cmd); /* Do not return data */ if (p2 == CTBCS_P2_REQUEST_NO_RESP) { if (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_ICC_ASYNC) { buffer[0] = CTBCS_SW1_REQUEST_ASYNC_OK; buffer[1] = CTBCS_SW2_REQUEST_ASYNC_OK; length = 2; } else { buffer[0] = CTBCS_SW1_REQUEST_SYNC_OK; buffer[1] = CTBCS_SW2_REQUEST_SYNC_OK; length = 2; } } /* Return whole atr */ else if (p2 == CTBCS_P2_REQUEST_GET_ATR) { atr = CT_Slot_GetAtr (ct->slots[sn]); if (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_ICC_ASYNC) { if (atr != NULL) ATR_GetRaw ((ATR *) atr, buffer, &length); else length = 0; buffer[length] = CTBCS_SW1_REQUEST_ASYNC_OK; buffer[length + 1] = CTBCS_SW2_REQUEST_ASYNC_OK; length += 2; } else { if (atr != NULL) ATR_Sync_GetRaw ((ATR_Sync *) atr, buffer, &length); else length = 0; buffer[length] = CTBCS_SW1_REQUEST_SYNC_OK; buffer[length +1] = CTBCS_SW2_REQUEST_SYNC_OK; length += 2; } } /* Return historical bytes */ else if (p2 == CTBCS_P2_REQUEST_GET_HIST) { atr = CT_Slot_GetAtr (ct->slots[sn]); if (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_ICC_ASYNC) { if (atr != NULL) ATR_GetHistoricalBytes ((ATR *) atr, buffer, &length); else length = 0; buffer[length] = CTBCS_SW1_REQUEST_ASYNC_OK; buffer[length + 1] = CTBCS_SW2_REQUEST_ASYNC_OK; length += 2; } else { if (atr != NULL) ATR_Sync_GetHistoricalBytes ((ATR_Sync *) atr, buffer, &length); else length = 0; buffer[length] = CTBCS_SW1_REQUEST_SYNC_OK; buffer[length + 1] = CTBCS_SW2_REQUEST_SYNC_OK; } } /* Wrong command cualifier */ else { length = 2; buffer[0] = CTBCS_SW1_WRONG_PARAM; buffer[1] = CTBCS_SW2_WRONG_PARAM; ret = OK; } } /* Wrong functional unit */ else { length = 2; buffer[0] = CTBCS_SW1_WRONG_PARAM; buffer[1] = CTBCS_SW2_WRONG_PARAM; ret = OK; } (*rsp) = APDU_Rsp_New (buffer, (long)length); return ret; } static char CardTerminal_GetStatus (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp) { BYTE buffer[CARDTERMINAL_GETSTATUS_BUFFER_SIZE], p1, p2; bool card, change; int i; unsigned length; char ret = OK; /* Get functional unit */ p1 = APDU_Cmd_P1 (cmd); /* Wrong functional unit */ if (p1 != CTBCS_P1_CT_KERNEL) { length = 2; buffer[0] = CTBCS_SW1_WRONG_PARAM; buffer[1] = CTBCS_SW2_WRONG_PARAM; ret = OK; } /* Get command cualifier */ p2 = APDU_Cmd_P2 (cmd); if (p2 == CTBCS_P2_STATUS_MANUFACTURER) { length = 17; /* CT Manufacturer */ memcpy(buffer,CARDTERMINAL_MANUFACTURER,5); /* CT type */ if (ct->slots[0] != NULL) CT_Slot_GetType(ct->slots[0], buffer + 5,5); /* CT version */ memcpy(buffer+10,VERSION,5); buffer[15] = CTBCS_SW1_OK; buffer[16] = CTBCS_SW2_OK; ret = OK; } else if (p2 == CTBCS_P2_STATUS_ICC) { for (i = 0; i < ct->num_slots; i++) { ret = CT_Slot_Check (ct->slots[i], 0, &card, &change); if (ret != OK) { /* There are no status bytes defined to be returned on error */ (*rsp) = NULL; return ret; } /* Resynchronise the driver status with the actual status of slot */ if ((CT_Slot_GetICCType (ct->slots[i]) != CT_SLOT_NULL) && (!card || change)) { ret = CT_Slot_Release (ct->slots[i]); if (ret != OK) { (*rsp) = NULL; return ret; } } buffer[i] = card? CTBCS_DATA_STATUS_CARD_CONNECT: CTBCS_DATA_STATUS_NOCARD; } length = i+2; buffer[i] = CTBCS_SW1_OK; buffer[i+1] = CTBCS_SW2_OK; } /* Wrong command cualifier */ else { length = 2; buffer[0] = CTBCS_SW1_WRONG_PARAM; buffer[1] = CTBCS_SW2_WRONG_PARAM; ret = OK; } (*rsp) = APDU_Rsp_New (buffer, length); return ret; } static char CardTerminal_EjectICC (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp) { BYTE buffer[CARDTERMINAL_EJECTICC_BUFFER_SIZE], p1, p2; int sn, timeout; unsigned length; bool card, change; char ret; /* Get functional unit */ p1 = APDU_Cmd_P1 (cmd); /* Wrong functional unit */ if ((p1 != CTBCS_P1_INTERFACE1) && (p1 != CTBCS_P1_INTERFACE2)) { buffer[0] = CTBCS_SW1_WRONG_PARAM; buffer[1] = CTBCS_SW2_WRONG_PARAM; (*rsp) = APDU_Rsp_New (buffer, 2); return OK; } /* Get the slot number */ sn = (p1 == CTBCS_P1_INTERFACE1) ? 0 : 1; if (!(sn < ct->num_slots)) { buffer[0] = CTBCS_SW1_WRONG_PARAM; buffer[1] = CTBCS_SW2_WRONG_PARAM; (*rsp) = APDU_Rsp_New (buffer, 2); return ERR_INVALID; } /* Get command cualifier */ p2 = APDU_Cmd_P2 (cmd); #if 0 /* Wrong command cualifier */ if (p2 != 0) { buffer[0] = CTBCS_SW1_WRONG_PARAM; buffer[1] = CTBCS_SW2_WRONG_PARAM; (*rsp) = APDU_Rsp_New (buffer, 2); return OK; } #endif if (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_NULL) { buffer[0] = CTBCS_SW1_EJECT_OK; buffer[1] = CTBCS_SW2_EJECT_OK; (*rsp) = APDU_Rsp_New (buffer, 2); return OK; } /* Get the card removal timeout */ if (APDU_Cmd_Lc (cmd) == 1) timeout = (int) (*APDU_Cmd_Data (cmd)); else timeout = 0; /* Check for card removal */ ret = CT_Slot_Check (ct->slots[sn], timeout, &card, &change); if (ret != OK) { (*rsp) = NULL; return ret; } /* Release ICC (always or only when card is removed?) */ ret = CT_Slot_Release (ct->slots[sn]); if (ret != OK) { (*rsp) = NULL; return ret; } if (timeout != 0) { if (card) { buffer[0] = CTBCS_SW1_EJECT_NOT_REMOVED; buffer[1] = CTBCS_SW2_EJECT_NOT_REMOVED; length = 2; } else { buffer[0] = CTBCS_SW1_EJECT_REMOVED; buffer[1] = CTBCS_SW2_EJECT_REMOVED; length = 2; } } else { buffer[0] = CTBCS_SW1_EJECT_OK;; buffer[1] = CTBCS_SW2_EJECT_OK;; length = 2; } (*rsp) = APDU_Rsp_New (buffer, length); return ret; } static void CardTerminal_Clear (CardTerminal * ct) { int i; ct->io = NULL; ct->num_slots = 0; for (i = 0; i < CARDTERMINAL_MAX_SLOTS; i++) ct->slots[i] = NULL; }