/* * * Copyright 2016-2020 NXP * SPDX-License-Identifier: Apache-2.0 */ /* * * @par History * 1.0 1-oct-2016 : Initial version * * *****************************************************************************/ /** * @file sm_connect.c * @par Description * Implementation of basic communication functionality between Host and A71CH. * (This file was renamed from ``a71ch_com.c`` into ``sm_connect.c``.) */ #if defined(SSS_USE_FTR_FILE) #include "fsl_sss_ftr.h" #else #include "fsl_sss_ftr_default.h" #endif #include #include #include #include #include #include "sm_api.h" #include "sm_apdu.h" #include "sm_errors.h" #include "sm_types.h" #include "nxLog_smCom.h" #include "nxEnsure.h" /// @cond //Also do select after opening the connection #define OPEN_AND_SELECT 0 /// @endcond #ifdef TDA8029_UART #include "smComAlpar.h" #include "smUart.h" #endif #if defined(SCI2C) #include "smComSCI2C.h" #endif #if defined(SPI) #include "smComSCSPI.h" #endif #if defined(PCSC) #include "smComPCSC.h" #endif #if defined(SMCOM_JRCP_V1) #include "smComSocket.h" #endif #if defined(SMCOM_JRCP_V2) #include "smComJRCP.h" #endif #if defined(RJCT_VCOM) #include "smComSerial.h" #endif #if defined(T1oI2C) #include "smComT1oI2C.h" #endif #if defined(SMCOM_PN7150) #include "smComPN7150.h" #endif #if defined(SMCOM_THREAD) #include "smComThread.h" #endif #if defined(SMCOM_PCSC) #include "smComPCSC.h" #endif #if defined(SMCOM_RC663_VCOM) #include "smComNxpNfcRdLib.h" #endif #include "global_platf.h" /// @cond Optional diagnostics functionality // #define FLOW_VERBOSE #ifdef FLOW_VERBOSE #define FPRINTF(...) printf(__VA_ARGS__) #else #define FPRINTF(...) #endif /// @endcond #if defined(SMCOM_JRCP_V1) || defined(SMCOM_JRCP_V2) static U16 getSocketParams(const char *arg, U8 *szServer, U16 szServerLen, unsigned int *port) { // the IP address is in format a.b.c.d:port, e.g. 10.0.0.1:8080 int nSuccess; U16 rv = SW_OK; ENSURE_OR_EXIT_WITH_STATUS_ON_ERROR(strlen(arg) < szServerLen, rv, ERR_BUF_TOO_SMALL); // First attempt at parsing: server IP-address passed, sscanf will return 2 upon successfull parsing nSuccess = sscanf(arg, "%15[0-9.]:%5u[0-9]", szServer, (unsigned int *)port); if (nSuccess == 2) { return SW_OK; } else { // Second attempt at parsing: server name passed instead of IP-address unsigned int i; int fColonFound = 0; for (i = 0; i < strlen(arg); i++) { if (arg[i] == ':') { szServer[i] = 0; fColonFound = 1; // PRINTF("servername: %s\r\n", szServer); break; } else { szServer[i] = arg[i]; } } if ((fColonFound == 1) && (i != 0)) { nSuccess = sscanf(&arg[i], ":%5u[0-9]", (unsigned int *)port); ENSURE_OR_EXIT_WITH_STATUS_ON_ERROR(nSuccess != 1, rv, SW_OK); } } rv = ERR_NO_VALID_IP_PORT_PATTERN; exit: return rv; } /** * Establishes communication with the Security Module via a Remote JC Terminal Server * (RJCT-Server). * Next it will invoke ::SM_Connect and select the A71CH applet on the Secure Module * * \note Because connecting via an RJCT-server requires an extra parameter (the server IP:Port) * an additional function is required on top of ::SM_Connect * * @param[in,out] connectString ip:port as string * @param[in,out] commState * @param[in,out] atr * @param[in,out] atrLen * * @retval ::SW_OK Upon successful execution */ U16 SM_RjctConnectSocket(void **conn_ctx, const char *connectString, SmCommState_t *commState, U8 *atr, U16 *atrLen) { U8 szServer[128]; U16 szServerLen = sizeof(szServer); U16 rv = 0; unsigned int port = 0; #if defined(SMCOM_JRCP_V2) char hostname[32] = {0}; #endif #ifndef A71_IGNORE_PARAM_CHECK if ((connectString == NULL) || (commState == NULL) || (atr == NULL) || (atrLen == 0)) { return ERR_API_ERROR; } #endif rv = getSocketParams(connectString, szServer, szServerLen, (unsigned int *)&port); #if defined(SMCOM_JRCP_V1) FPRINTF("Connection to secure element over socket to %s\r\n", connectString); if (rv != SW_OK) { return rv; } // NOTE-MMA: The usage of the sss type kType_SE_Conn_Type_JRCP_V1 leads to a circular // dependency regarding the inclusion of header files. // if (commState->connType == kType_SE_Conn_Type_JRCP_V1) { rv = smComSocket_Open(conn_ctx, szServer, (U16)port, atr, atrLen); // } #endif #if defined(SMCOM_JRCP_V2) if (commState->connType == kType_SE_Conn_Type_JRCP_V2) { if (sizeof(hostname) < strlen(connectString)) { return ERR_API_ERROR; } strncpy(hostname, connectString, strlen(connectString)); rv = smComJRCP_Open(conn_ctx, strtok(hostname, ":"), port); } #endif if (rv != SMCOM_OK) { LOG_E("Error on smComSocket_Open: 0x%04X\r\n", rv); return rv; } if (conn_ctx == NULL) { rv = SM_Connect(NULL, commState, atr, atrLen); } else { rv = SM_Connect(*conn_ctx, commState, atr, atrLen); } return rv; } #endif /* defined(SMCOM_JRCP_V1) || defined (SMCOM_JRCP_V2) */ #ifdef RJCT_VCOM U16 SM_RjctConnectVCOM(void **conn_ctx, const char *connectString, SmCommState_t *commState, U8 *atr, U16 *atrLen) { U32 status; #ifndef A71_IGNORE_PARAM_CHECK if ((connectString == NULL) || (commState == NULL) || (atr == NULL) || (atrLen == 0)) { return ERR_API_ERROR; } #endif status = smComVCom_Open(conn_ctx, connectString); #if SSS_HAVE_APPLET_SE051_UWB if (status == 0 || status == SMCOM_COM_ALREADY_OPEN) { #else if (status == 0) { #endif if (conn_ctx == NULL) { status = smComVCom_GetATR(NULL, atr, atrLen); if (status == 0) { status = (U16)SM_Connect(NULL, commState, atr, atrLen); if (status != SMCOM_OK) { SM_Close(NULL, 0); } } else { SM_Close(NULL, 0); } } else { status = smComVCom_GetATR(*conn_ctx, atr, atrLen); if (status == 0) { status = (U16)SM_Connect(*conn_ctx, commState, atr, atrLen); } else { SM_Close(NULL, 0); } } } else { *atrLen = 0; } return (U16)status; } #endif // RJCT_VCOM #ifdef SMCOM_RC663_VCOM U16 SM_RjctConnectNxpNfcRdLib(void **conn_ctx, const char *connectString, SmCommState_t *commState, U8 *atr, U16 *atrLen) { U32 status; if ((connectString == NULL) || (commState == NULL) || (atr == NULL) || (atrLen == 0)) { return ERR_API_ERROR; } status = smComNxpNfcRdLib_OpenVCOM(conn_ctx, connectString); if (status == 0) { status = (U16)SM_Connect(conn_ctx, commState, atr, atrLen); } else { *atrLen = 0; } if (status == SMCOM_OK) { *atrLen = 0; } return (U16)status; } #endif #ifdef SMCOM_PCSC U16 SM_RjctConnectPCSC(void **conn_ctx, const char *connectString, SmCommState_t *commState, U8 *atr, U16 *atrLen) { U32 status = SMCOM_OK; #ifndef A71_IGNORE_PARAM_CHECK if ( //(connectString == NULL) || (commState == NULL) || (atr == NULL) || (atrLen == 0)) { return ERR_API_ERROR; } #endif status = smComPCSC_Open(connectString); if (status == SMCOM_OK) { if (conn_ctx == NULL) { status = (U16)SM_Connect(NULL, commState, atr, atrLen); } else { status = (U16)SM_Connect(*conn_ctx, commState, atr, atrLen); } } else { *atrLen = 0; } return (U16)status; } #endif // RJCT_VCOM U16 SM_RjctConnect(void **conn_ctx, const char *connectString, SmCommState_t *commState, U8 *atr, U16 *atrLen) { #if RJCT_VCOM || SMCOM_JRCP_V1 || SMCOM_JRCP_V2 || SMCOM_RC663_VCOM bool is_socket = FALSE; bool is_vcom = FALSE; AX_UNUSED_ARG(is_socket); AX_UNUSED_ARG(is_vcom); #endif #if RJCT_VCOM || SMCOM_RC663_VCOM if (NULL == connectString) { is_vcom = FALSE; } else if (0 == strncmp("COM", connectString, sizeof("COM") - 1)) { is_vcom = TRUE; } else if (0 == strncmp("\\\\.\\COM", connectString, sizeof("\\\\.\\COM") - 1)) { is_vcom = TRUE; } else if (0 == strncmp("/tty/", connectString, sizeof("/tty/") - 1)) { is_vcom = TRUE; } else if (0 == strncmp("/dev/tty", connectString, sizeof("/dev/tty") - 1)) { is_vcom = TRUE; } #endif #if SMCOM_JRCP_V1 || SMCOM_JRCP_V2 if (NULL == connectString) { LOG_W("connectString is NULL. Aborting."); return ERR_NO_VALID_IP_PORT_PATTERN; } if (NULL != strchr(connectString, ':')) { is_socket = TRUE; } #endif #if RJCT_VCOM if (is_vcom) { return SM_RjctConnectVCOM(conn_ctx, connectString, commState, atr, atrLen); } else { LOG_W("Build is compiled for VCOM. connectString='%s' does not look like COMPort",connectString); LOG_W("e.g. connectString are COM3, \\\\.\\COM5, /dev/tty.usbmodem1432301, etc."); } #endif #if SMCOM_RC663_VCOM if (is_vcom) { return SM_RjctConnectNxpNfcRdLib(conn_ctx, connectString, commState, atr, atrLen); } else { LOG_W("Build is compiled for RC663_VCOM. connectString='%s' does not look like COMPort",connectString); LOG_W("e.g. connectString are COM3, \\\\.\\COM5, /dev/tty.usbmodem1432301, etc."); } #endif #if SMCOM_JRCP_V1 || SMCOM_JRCP_V2 if (is_socket) { return SM_RjctConnectSocket(conn_ctx, connectString, commState, atr, atrLen); } #endif #if SMCOM_PCSC if (NULL != commState) { return SM_RjctConnectPCSC(conn_ctx, connectString, commState, atr, atrLen); } #endif LOG_W( "Can not use connectString='%s' in the current build configuration.\n\tPlease select correct smCom interface " "and re-compile!\n", connectString); return ERR_NO_VALID_IP_PORT_PATTERN; } #if defined(SMCOM_JRCP_V1) || defined(SMCOM_JRCP_V2) || defined(RJCT_VCOM) || \ defined(SMCOM_PCSC) #else U16 SM_I2CConnect(void **conn_ctx, SmCommState_t *commState, U8 *atr, U16 *atrLen, const char *pConnString) { U16 status = SMCOM_COM_FAILED; #if defined(T1oI2C) status = smComT1oI2C_Init(conn_ctx, pConnString); #elif defined (SCI2C) status = smComSCI2C_Init(conn_ctx, pConnString); #endif if (status != SMCOM_OK) { return status; } if (conn_ctx == NULL) { return SM_Connect(NULL, commState, atr, atrLen); } else { return SM_Connect(*conn_ctx, commState, atr, atrLen); } } #endif /** * Establishes the communication with the Security Module (SM) at the link level and * selects the A71CH applet on the SM. The physical communication layer used (e.g. I2C) * is determined at compilation time. * * @param[in,out] commState * @param[in,out] atr * @param[in,out] atrLen * * @retval ::SW_OK Upon successful execution */ U16 SM_Connect(void *conn_ctx, SmCommState_t *commState, U8 *atr, U16 *atrLen) { U16 sw = SW_OK; #if !defined(IPC) #ifdef APPLET_NAME unsigned char appletName[] = APPLET_NAME; #endif // APPLET_NAME #ifdef SSD_NAME unsigned char ssdName[] = SSD_NAME; #endif U16 selectResponseDataLen = 0; U8 selectResponseData[256] = {0}; U16 uartBR = 0; U16 t1BR = 0; #endif #ifdef TDA8029_UART U32 status = 0; #endif #ifndef A71_IGNORE_PARAM_CHECK ENSURE_OR_EXIT_WITH_STATUS_ON_ERROR(((commState != NULL) && (atr != NULL) && (atrLen != 0)), sw, ERR_API_ERROR) #endif #ifdef TDA8029_UART if ((*atrLen) <= 33) return ERR_API_ERROR; smComAlpar_Init(); status = smComAlpar_AtrT1Configure(ALPAR_T1_BAUDRATE_MAX, atr, atrLen, &uartBR, &t1BR); if (status != SMCOM_ALPAR_OK) { commState->param1 = 0; commState->param2 = 0; FPRINTF("smComAlpar_AtrT1Configure failed: 0x%08X\r\n", status); return ERR_CONNECT_LINK_FAILED; } #elif defined SMCOM_PN7150 sw = smComPN7150_Open(0, 0x00, atr, atrLen); #elif defined(SCI2C) sw = smComSCI2C_Open(conn_ctx, ESTABLISH_SCI2C, 0x00, atr, atrLen); #elif defined(SPI) smComSCSPI_Init(ESTABLISH_SCI2C, 0x00, atr, atrLen); #elif defined(T1oI2C) sw = smComT1oI2C_Open(conn_ctx, ESE_MODE_NORMAL, 0x00, atr, atrLen); #elif defined(SMCOM_JRCP_V1) || defined(SMCOM_JRCP_V2) || defined(PCSC) || defined(SMCOM_PCSC) if (atrLen != NULL) *atrLen = 0; AX_UNUSED_ARG(atr); AX_UNUSED_ARG(atrLen); #elif defined(RJCT_VCOM) #elif defined(SMCOM_THREAD) sw = smComThread_Open(atr, atrLen); #endif // TDA8029_UART #if !defined(IPC) commState->param1 = t1BR; commState->param2 = uartBR; commState->hostLibVersion = (AX_HOST_LIB_MAJOR << 8) + AX_HOST_LIB_MINOR; commState->appletVersion = 0xFFFF; commState->sbVersion = 0xFFFF; #ifdef APPLET_NAME if (sw == SMCOM_OK) { selectResponseDataLen = sizeof(selectResponseData); /* CARD */ if (commState->select == SELECT_NONE) { /* Use Case just Connect to SE (smCom) and no kind of applet selection */ sw = SMCOM_OK; selectResponseDataLen = 0; } else if (commState->select == SELECT_SSD) { #ifdef SSD_NAME /* Rotate keys Use Case Connect to SE and Select SSD */ /* Select SSD */ sw = GP_Select(conn_ctx, (U8 *)&ssdName, sizeof(ssdName), selectResponseData, &selectResponseDataLen); #else sw = SMCOM_COM_FAILED; #endif } else { #if SSS_HAVE_A71CH || SSS_HAVE_A71CH_SIM || SSS_HAVE_A71CL /* Select card manager */ GP_Select(conn_ctx, (U8 *)&appletName, 0, selectResponseData, &selectResponseDataLen); selectResponseDataLen = sizeof(selectResponseData); #endif /* Select the applet */ sw = GP_Select(conn_ctx, (U8 *)&appletName, APPLET_NAME_LEN, selectResponseData, &selectResponseDataLen); } if (sw == SW_FILE_NOT_FOUND) { // Applet can not be selected (most likely it is simply not installed) LOG_E("Can not select Applet=%s'", SE_NAME); LOG_MAU8_E("Failed (SW_FILE_NOT_FOUND) selecting Applet. ", appletName, APPLET_NAME_LEN); return sw; } else if (sw != SW_OK) { LOG_E("SM_CONNECT Failed."); sw = ERR_CONNECT_SELECT_FAILED; } else { #ifdef FLOW_VERBOSE if (selectResponseDataLen > 0) { LOG_MAU8_I("selectResponseData", selectResponseData, selectResponseDataLen); } #endif // FLOW_VERBOSE #if SSS_HAVE_A71CH || SSS_HAVE_A71CH_SIM if (selectResponseDataLen >= 2) { commState->appletVersion = (selectResponseData[0] << 8) + selectResponseData[1]; if (selectResponseDataLen == 4) { commState->sbVersion = (selectResponseData[2] << 8) + selectResponseData[3]; } else if (selectResponseDataLen == 2) { commState->sbVersion = 0x0000; } } else { sw = ERR_CONNECT_SELECT_FAILED; } #elif SSS_HAVE_A71CL if (selectResponseDataLen == 0) { commState->appletVersion = 0; commState->sbVersion = 0x0000; } #endif // SSS_HAVE_A71CH / SSS_HAVE_A71CL #if SSS_HAVE_SE05X if (selectResponseDataLen == 5 || selectResponseDataLen == 4 || selectResponseDataLen == 7) { // 2.2.4 returns 4 bytes, 2.2.4.[A,B,C] // 2.3.0 returns 5 bytes, 2.3.0.[v1].[v2] // 2.5.3 returns 7 bytes, commState->appletVersion = 0; commState->appletVersion |= selectResponseData[0]; commState->appletVersion <<= 8; commState->appletVersion |= selectResponseData[1]; commState->appletVersion <<= 8; commState->appletVersion |= selectResponseData[2]; commState->appletVersion <<= 8; // commState->appletVersion |= selectResponseData[3]; commState->sbVersion = 0x0000; } else { } #endif // SSS_HAVE_SE05X } } #endif /* Applet Name*/ #endif // !defined(IPC) exit: return sw; } /** * Closes the communication with the Security Module * A new connection can be established by calling ::SM_Connect * * @param[in] mode Specific information that may be required on the link layer * * @retval ::SW_OK Upon successful execution */ U16 SM_Close(void *conn_ctx, U8 mode) { U16 sw = SW_OK; #if defined(SCI2C) sw = smComSCI2C_Close(mode); #endif #if defined(SPI) sw = smComSCSPI_Close(mode); #endif #if defined(PCSC) sw = smComPCSC_Close(mode); #endif #if defined(T1oI2C) sw = smComT1oI2C_Close(conn_ctx, mode); #endif #if defined(SMCOM_JRCP_V1) AX_UNUSED_ARG(mode); sw = smComSocket_Close(); #endif #if defined(SMCOM_JRCP_V2) AX_UNUSED_ARG(mode); sw = smComJRCP_Close(conn_ctx, mode); #endif #if defined(RJCT_VCOM) AX_UNUSED_ARG(mode); sw = smComVCom_Close(conn_ctx); #endif #if defined(SMCOM_THREAD) AX_UNUSED_ARG(mode); sw = smComThread_Close(); #endif #if defined(SMCOM_RC663_VCOM) AX_UNUSED_ARG(mode); smComNxpNfcRdLib_Close(); #endif smCom_DeInit(); return sw; } /** * Sends the command APDU to the Secure Module and retrieves the response APDU. * The latter consists of the concatenation of the response data (possibly none) and the status word (2 bytes). * * The command APDU and response APDU are not interpreted by the host library. * * The command/response APDU sizes must lay within the APDU size limitations * * @param[in] cmd command APDU * @param[in] cmdLen length (in byte) of \p cmd * @param[in,out] resp response APDU (response data || response status word) * @param[in,out] respLen IN: Length of resp buffer (\p resp) provided; OUT: effective length of response retrieved. * * @retval ::SW_OK Upon successful execution */ U16 SM_SendAPDU(U8 *cmd, U16 cmdLen, U8 *resp, U16 *respLen) { U32 status = 0; U32 respLenLocal; #ifndef A71_IGNORE_PARAM_CHECK ENSURE_OR_RETURN_ON_ERROR(((cmd != NULL) && (resp != NULL) && (respLen != NULL)), ERR_API_ERROR); #endif respLenLocal = *respLen; status = smCom_TransceiveRaw(NULL, cmd, cmdLen, resp, &respLenLocal); *respLen = (U16)respLenLocal; return (U16)status; } #if defined(SMCOM_JRCP_V1_AM) U16 SM_LockChannel() { return smComSocket_LockChannel(); } U16 SM_UnlockChannel() { return smComSocket_UnlockChannel(); } #endif