/** * @file smComPCSC.c * @author NXP Semiconductors * @version 1.0 * @section LICENSE * ---------------------------------------------------------------------------- * * Copyright 2016,2019,2020 NXP * SPDX-License-Identifier: Apache-2.0 * ---------------------------------------------------------------------------- * @section DESCRIPTION * This file implements the SmCom PCSC communication layer. * ---------------------------------------------------------------------------- * *****************************************************************************/ #include #include #include "sm_apdu.h" #include "smComPCSC.h" #include "sm_printf.h" #include "nxEnsure.h" #include #ifdef __OSX_AVAILABLE #include #include #elif defined(__linux__) #include #include #else #include #endif //#include #ifdef FLOW_VERBOSE #define NX_LOG_ENABLE_SMCOM_DEBUG 1 #else //#define NX_LOG_ENABLE_SMCOM_DEBUG 1 #endif #include "nxLog_smCom.h" static U32 smComPCSC_Transceive(void* conn_ctx, apdu_t *pApdu); static U32 smComPCSC_TransceiveRaw(void* conn_ctx, U8 *pTx, U16 txLen, U8 *pRx, U32 *pRxLen); static SCARDHANDLE hCard; static SCARD_IO_REQUEST pioSendPci; #ifdef FLOW_VERBOSE #define PCSC_APDU_VERBOSE #endif #if defined(__linux__) /* PC SC Lite has it */ #else #define pcsc_stringify_error(X) "pcsc_stringify_error" #endif U16 smComPCSC_Close(U8 mode) { return SW_OK; } U16 smComPCSC_Open(const char *reader_name_in) { LONG rv; SCARDCONTEXT hContext; DWORD dwReaders, dwActiveProtocol; LPTSTR mszReaders; LPTSTR selectedReader; U16 ret = SMCOM_COM_FAILED; rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); if (rv != SCARD_S_SUCCESS) { LOG_E("SCardEstablishContext failed: %x (%s)", rv, pcsc_stringify_error(rv)); goto exit; } #ifdef SCARD_AUTOALLOCATE dwReaders = SCARD_AUTOALLOCATE; rv = SCardListReaders(hContext, NULL, (LPTSTR)&mszReaders, &dwReaders); if (rv != SCARD_S_SUCCESS) { LOG_E("SCardListReaders(..SCARD_AUTOALLOCATE..) failed: %x (%s)", rv, pcsc_stringify_error(rv)); goto exit; } #else rv = SCardListReaders(hContext, NULL, NULL, &dwReaders); if (rv != SCARD_S_SUCCESS) { LOG_E("SCardListReaders(..SCARD_AUTOALLOCATE..) failed: %x (%s)", rv, pcsc_stringify_error(rv)); goto exit; } dwReaders = dwReaders * 255; mszReaders = calloc(dwReaders, sizeof(char)); rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders); if (rv != SCARD_S_SUCCESS) { LOG_E("SCardListReaders(..SCARD_AUTOALLOCATE..) failed: %x (%s)", rv, pcsc_stringify_error(rv)); goto exit; } #endif if (rv == SCARD_E_NO_READERS_AVAILABLE) { LOG_E("Reader is not in groups."); goto exit; } else if (rv == SCARD_S_SUCCESS) { LPTSTR pReader; // Default to First found reader.. selectedReader = mszReaders; pReader = mszReaders; while ('\0' != *pReader) { // Display the value. // But we only connect to the first reader. LOG_I("Found Reader: %s", pReader); if (NULL != reader_name_in) { if (0 != strstr(pReader, reader_name_in)) { selectedReader = pReader; } } // Advance to the next value. #ifdef UNICODE pReader = pReader + wcslen((wchar_t *)pReader) + 1; #else pReader = pReader + strlen(pReader) + 1; #endif } } else { LOG_E("Failed SCardListReaders"); goto exit; } LOG_I("Connecting to reader: %s", selectedReader); rv = SCardConnect( hContext, selectedReader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol); #ifdef SCARD_AUTOALLOCATE LONG rvFree = SCardFreeMemory(hContext, mszReaders); if (SCARD_S_SUCCESS != rvFree) { LOG_E("Failed SCardFreeMemory"); goto exit; } #else free(mszReaders); #endif if (rv != SCARD_S_SUCCESS) { LOG_E("SCardConnect() failed: %x (%s)", rv, pcsc_stringify_error(rv)); goto exit; } switch (dwActiveProtocol) { case SCARD_PROTOCOL_T0: memcpy(&pioSendPci, SCARD_PCI_T0, sizeof(pioSendPci)); LOG_D("T=0 protocol"); break; case SCARD_PROTOCOL_T1: memcpy(&pioSendPci, SCARD_PCI_T1, sizeof(pioSendPci)); LOG_D("T=1 protocol"); break; } return smCom_Init(smComPCSC_Transceive, smComPCSC_TransceiveRaw); exit: return ret; } static U32 smComPCSC_Transceive(void* conn_ctx, apdu_t *pApdu) { U32 rxLen = MAX_APDU_BUF_LENGTH; U32 status = SMCOM_SND_FAILED; ENSURE_OR_GO_EXIT(pApdu != NULL); status = smComPCSC_TransceiveRaw(conn_ctx, pApdu->pBuf, pApdu->buflen, pApdu->pBuf, &rxLen); pApdu->rxlen = rxLen; // reset offset for subsequent response parsing pApdu->offset = 0; exit: return status; } // #define PCSC_APDU_VERBOSE // Define in Makefile if required static U32 smComPCSC_TransceiveRaw(void* conn_ctx, U8 *pTx, U16 txLen, U8 *pRx, U32 *pRxLen) { DWORD dwRecvLength; LONG rv; U32 ret = SMCOM_COM_FAILED; ENSURE_OR_GO_EXIT(pRxLen != NULL); ENSURE_OR_GO_EXIT(pRx != NULL); ENSURE_OR_GO_EXIT(pTx != NULL); LOG_AU8_D(pTx, txLen); //LPBYTE pbAttr = NULL; //DWORD cByte = 0; //rv = SCardGetAttrib(hCard, SCARD_ATTR_CURRENT_BWT, (LPBYTE)&pbAttr, &cByte); //cByte += 10; //rv = SCardSetAttrib(hCard, SCARD_ATTR_CURRENT_BWT, (LPBYTE)&pbAttr, cByte); dwRecvLength = *pRxLen; if (0 == ((txLen + 10) % 64)) { /* USB 64 byte boundary =================================== If data block is multiple of 64 bytes ccid expects next block of data and gets stuck in the bulk out. Workaround to solve this problem: add 1 extra 0x00 byte to the final command. For this to work, the incoming buffer must not be from a constant array, and must have one extra byte at the end of the buffer. */ pTx[txLen] = 0x00; txLen += 1; } rv = SCardTransmit(hCard, &pioSendPci, pTx, txLen, NULL, pRx, &dwRecvLength); if (rv != SCARD_S_SUCCESS) { *pRxLen = 0; LOG_E("SCardTransmit() failed: %x (%s)", rv, pcsc_stringify_error(rv)); return SMCOM_COM_FAILED; } else { *pRxLen = dwRecvLength; LOG_AU8_D(pRx, dwRecvLength); } ret = SMCOM_OK; exit: return ret; }