blob: ebc80f50f878541c32ffe8f9e39c7962f4a78e8a [file] [log] [blame]
/*
*
* 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 <sm_const.h>
#include <smCom.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#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(IPC)
#include "smComIpc.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 (status == 0) {
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(IPC)
sw = smComIpc_Open(atr, atrLen, &(commState->hostLibVersion), &(commState->appletVersion), &(commState->sbVersion));
#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(IPC)
AX_UNUSED_ARG(mode);
sw = smComIpc_Close();
#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(IPC)
U16 SM_LockChannel()
{
return smComIpc_LockChannel();
}
U16 SM_UnlockChannel()
{
return smComIpc_UnlockChannel();
}
#endif