blob: 6e57ef3f1541e981921a90589c8096c887f00c30 [file] [log] [blame]
/**
* @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 <assert.h>
#include <stddef.h>
#include "sm_apdu.h"
#include "smComPCSC.h"
#include "sm_printf.h"
#include "nxEnsure.h"
#include <string.h>
#ifdef __OSX_AVAILABLE
#include <PCSC/wintypes.h>
#include <PCSC/winscard.h>
#elif defined(__linux__)
#include <PCSC/wintypes.h>
#include <PCSC/winscard.h>
#else
#include <winscard.h>
#endif
//#include <wintypes.h>
#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;
}