673 lines
22 KiB
C
Raw Normal View History

2022-04-28 02:04:02 +02:00
/**
******************************************************************************
* @file demo_ce.c
* @author MMY Application Team
* @brief Body function to manage card emul mode
******************************************************************************
** This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* COPYRIGHT(c) 2018 STMicroelectronics
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "demo_ce.h"
#include "utils.h"
#include "st_errno.h"
#include "rfal_rf.h"
#include "rfal_nfca.h"
#include "rfal_nfcf.h"
/** @addtogroup X-CUBE-NFC6_Applications
* @{
*/
/** @addtogroup CardEmulation
* @{
*/
/** @addtogroup CE_CardEmul
* @{
*/
/* Private typedef -----------------------------------------------------------*/
/** @defgroup CE_CardEmul_Private_Typedef
* @{
*/
enum States
{
STATE_IDLE = 0, /*!< Emulated Tag state idle */
STATE_APP_SELECTED = 1, /*!< Emulated Tag state application selected */
STATE_CC_SELECTED = 2, /*!< Emulated Tag state CCFile selected */
STATE_FID_SELECTED = 3, /*!< Emulated Tag state FileID selected */
};
/**
* @}
*/
/* Private define ------------------------------------------------------------*/
/** @defgroup CE_CardEmul_Private_Define
* @{
*/
#define NDEF_SIZE 0xFFFE /*!< Max NDEF size emulated */
#define T4T_CLA_00 0x00 /*!< CLA value for type 4 command */
#define T4T_INS_SELECT 0xA4 /*!< INS value for select command */
#define T4T_INS_READ 0xB0 /*!< INS value for reabbinary command */
#define T4T_INS_UPDATE 0xD6 /*!< INS value for update command */
#define FID_CC 0xE103 /*!< File ID number for CCFile */
#define FID_NDEF 0xE104 /*!< File ID number for NDEF file */
#define T3T_BLOCK_SIZE 0x10 /*!< Block size in Type 3 Tag */
/**
* @}
*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/** @defgroup CE_CardEmul_Private_Variables
* @{
*/
static uint8_t gNfcfNfcid[RFAL_NFCF_NFCID2_LEN];
static uint8_t ndefFile[2048]; /*!< Buffer to store NDEF File */
static int8_t nState = STATE_IDLE; /*!< Type 4 tag emulation status */
static int32_t nSelectedIdx = -1; /*!< current file selected */
static int32_t nFiles = 2; /*!< Number of file emulated */
/**
* CCLEN : Indicates the size of this CC File <BR>
* T4T_VNo : Indicates the Mapping Version <BR>
* MLe high : Max R-APDU size <BR>
* MLc high : Max C-APDU size <BR>
* NDEF FCI T: Indicates the NDEF-File_Ctrl_TLV <BR>
* NDEF FCI L: The length of the V-field <BR>
* NDEF FCI V1: NDEF File Identifier <BR>
* NDEF FCI V2: NDEF File size <BR>
* NDEF FCI V3: NDEF Read AC <BR>
* NDEF FCI V4: NDEF Write AC <BR>
*/
static uint8_t ccfile[] = {
0x00, 0x0F, /* CCLEN */
0x20, /* T4T_VNo */
0x00, 0x7F, /* MLe */
0x00, 0x7F, /* MLc */
0x04, /* T */
0x06, /* L */
(FID_NDEF & 0xFF00) >> 8, (FID_NDEF & 0x00FF), /* V1 */
(NDEF_SIZE & 0xFF00) >> 8, (NDEF_SIZE & 0x00FF), /* V2 */
0x00, /* V3 */
0x00 /* V4 */
};
static uint32_t pdwFileSize[] = {sizeof(ccfile), NDEF_SIZE}; /*!< Current emulated files size */
/**
* NDEF length <BR>
* NDEF Header: MB,ME,SR,Well known Type <BR>
* NDEF type length <BR>
* NDEF payload length <BR>
* NDEF Type : URI <BR>
* NDEF URI abreviation field : http://www. <BR>
* NDEF URI string : st.com/st25r <BR>
* NDEF URI string : st.com/st25-demo <BR>
*/
static const uint8_t ndef_uri[] = { 0x00, 0x15, /* NDEF length */
0xD1, /* NDEF Header */
0x01, /* NDEF type length */
0x11, /* NDEF payload length */
0x55, /* NDEF Type */
0x01, /* NDEF URI abreviation field */
0x73, 0x74, 0x2E, 0x63, 0x6F, /* NDEF URI string */
0x6D, 0x2F, 0x73, 0x74, 0x32,
0x35, 0x2D, 0x64, 0x65, 0x6D, 0x6F };
/**
* Ver : Indicates the NDEF mapping versiom <BR>
* Nbr : Indicates the number of blocks that can be read <BR>
* Nbw : Indicates the number of blocks that can be written <BR>
* NmaxB : Indicates the maximum number of blocks available for NDEF data <BR>
* WriteFlag : Indicates whether a previous NDEF write procedure has finished or not <BR>
* RWFlag : Indicates data can be updated or not <BR>
* Ln : Is the size of the actual stored NDEF data in bytes <BR>
* Checksum : allows the Reader/Writer to check whether the Attribute Data are correct <BR>
*/
static uint8_t InformationBlock[] = { 0x10, /* Ver */
0x0F, /* Nbr */
0x0D, /* Nbw */
0x00, 0x0F, /* NmaxB */
0x00, 0x00, 0x00, 0x00, /* RFU */
0x00, /* WriteFlag */
0x01, /* RWFlag */
0x00, 0x00, 0x15, /* Ln */
0x00, 0x51 /* Checksum */
};
/**
* @}
*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/** @defgroup CE_CardEmul_Private_Functions
* @{
*/
/**
*****************************************************************************
* @brief Compare 2 commands supplied in parameters
*
* @param[in] cmd : pointer to the received command.
* @param[in] find : pointer to the avalaible command.
* @param[in] len : length of the available command.
*
* @retval True : Same command.
* @retval False : Different command.
*****************************************************************************
*/
static bool cmdCompare(uint8_t *cmd, uint8_t *find, uint16_t len)
{
for(int i = 0; i < 20; i++)
{
if(!memcmp(&cmd[i],find, len))
{
return true;
}
}
return false;
}
/**
*****************************************************************************
* @brief Manage the T4T Select answer to the reader
*
* @param[in] cmdData : pointer to the received command.
* @param[out] rspData : pointer to the answer to send.
*
* @return Answer size.
*****************************************************************************
*/
static uint16_t demoCeT4TSelect(uint8_t *cmdData, uint8_t *rspData)
{
bool success = false;
/*
* Cmd: CLA(1) | INS(1) | P1(1) | P2(1) | Lc(1) | Data(n) | [Le(1)]
* Rsp: [FCI(n)] | SW12
*
* Select App by Name NDEF: 00 A4 04 00 07 D2 76 00 00 85 01 01 00
* Select App by Name NDEF 4 ST: 00 A4 04 00 07 A0 00 00 00 03 00 00 00
* Select CC FID: 00 A4 00 0C 02 xx xx
* Select NDEF FID: 00 A4 00 0C 02 xx xx
*/
uint8_t aid[] = {0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01};
uint8_t fidCC[] = {FID_CC >> 8 , FID_CC & 0xFF};
uint8_t fidNDEF[] = {FID_NDEF >> 8, FID_NDEF & 0xFF};
uint8_t selectFileId[] = {0xA4, 0x00, 0x0C, 0x02, 0x00, 0x01 };
if(cmdCompare( cmdData, aid, sizeof(aid)))
{ /* Select Appli */
nState = STATE_APP_SELECTED;
success = true;
}
else if((nState >= STATE_APP_SELECTED) && cmdCompare(cmdData, fidCC, sizeof(fidCC)))
{ /* Select CC */
nState = STATE_CC_SELECTED;
nSelectedIdx = 0;
success = true;
}
else if((nState >= STATE_APP_SELECTED) && (cmdCompare(cmdData,fidNDEF,sizeof(fidNDEF)) || cmdCompare(cmdData,selectFileId,sizeof(selectFileId))))
{ /* Select NDEF */
nState = STATE_FID_SELECTED;
nSelectedIdx = 1;
success = true;
}
else
{
nState = STATE_IDLE;
}
rspData[0] = (success ? (char)0x90 : 0x6A);
rspData[1] = (success ? (char)0x00 : 0x82);
return 2;
}
/**
*****************************************************************************
* @brief Manage the T4T Read answer to the reader
*
* @param[in] cmdData : pointer to the received command.
* @param[out] rspData : pointer to the answer to send.
*
* @return Answer size.
*****************************************************************************
*/
static uint16_t demoCeT4TRead(uint8_t *cmdData, uint8_t *rspData)
{
/*
* Cmd: CLA(1) | INS(1) | P1(1).. offset inside file high | P2(1).. offset inside file high | Le(1).. nBytes to read
* Rsp: BytesRead | SW12
*/
unsigned short offset = (cmdData[2] << 8) | cmdData[3];
unsigned short toRead = cmdData[4];
uint8_t * ppbMemory;
/* Any file selected */
if(nSelectedIdx < 0 || nSelectedIdx >= nFiles)
{
rspData[0] = ((char)0x6A);
rspData[1] = ((char)0x82);
return 2;
}
/* offset + length exceed file size */
if((unsigned long)(offset + toRead) > pdwFileSize[nSelectedIdx])
{
toRead = pdwFileSize[nSelectedIdx] - offset;
}
ppbMemory = (nSelectedIdx == 0 ? ccfile : ndefFile);
/* read data */
memcpy(rspData, &ppbMemory[offset], toRead);
rspData[toRead] = ((char)0x90);
rspData[toRead+1] = ((char)0x00);
return toRead + 2;
}
/**
*****************************************************************************
* @brief Manage the T4T Update answer to the reader
*
* @param[in] cmdData : pointer to the received command.
* @param[in] rspData : pointer to the answer to send.
*
* @return Answer size.
*****************************************************************************
*/
static uint16_t demoCeT4TUpdate(uint8_t *cmdData, uint8_t *rspData)
{
uint32_t offset = (cmdData[2] << 8) | cmdData[3];
uint32_t length = cmdData[4];
if(nSelectedIdx != 1)
{
rspData[0] = ((char)0x6A);
rspData[1] = ((char)0x82);
return 2;
}
if((unsigned long)(offset + length) > pdwFileSize[nSelectedIdx])
{
rspData[0] = ((char)0x62);
rspData[1] = ((char)0x82);
return 2;
}
memcpy((ndefFile + offset), &cmdData[5], length);
rspData[0] = ((char)0x90);
rspData[1] = ((char)0x00);
return 2;
}
/**
*****************************************************************************
* @brief Manage the T4T Read answer to the reader
*
* @param[in] cmdData : pointer to the received command.
* @param[out] rspData : pointer to the answer to send.
*
* @return Answer size.
*****************************************************************************
*/
static uint16_t demoCeT3TCheck(uint8_t *cmdData, uint8_t *rspData)
{
/*
* Cmd: cmd | NFCID2 | NoS | Service code list | NoB | Block list
* Rsp: rsp | NFCID2 | Status Flag 1 | Status Flag 2 | NoB | Block Data
*/
uint8_t *block;
uint16_t blocknb[256];
uint32_t idx = 0;
uint32_t cnt = 0;
uint32_t nbmax = 0;
/* Command response */
rspData[idx++] = RFAL_NFCF_CMD_READ_WITHOUT_ENCRYPTION + 1;
/* NFCID 2 bytes */
if( ST_BYTECMP( gNfcfNfcid, &cmdData[ RFAL_NFCF_LENGTH_LEN + RFAL_NFCF_CMD_LEN ], RFAL_NFCF_NFCID2_LEN ) == 0 )
{
ST_MEMCPY( &rspData[idx], &gNfcfNfcid, RFAL_NFCF_NFCID2_LEN );
idx += RFAL_NFCF_NFCID2_LEN;
}
else
{
/* If NFCID2 in command is different, no answer */
return 0;
}
/* Check for command errors */
if( (cmdData[10] != 1) || ((cmdData[11] != 0x09) && (cmdData[11] != 0x0B))
|| (cmdData[13] == 0) || (cmdData[13] > InformationBlock[1]) )
{
rspData[idx++] = 0xFF;
rspData[idx++] = 0xFF;
return idx;
}
else
{
rspData[idx++] = 0x00;
rspData[idx++] = 0x00;
}
/* Nob */
rspData[idx++] = cmdData[13];
/* Retrieving block to read */
block = &cmdData[14];
for( cnt = 0; cnt < cmdData[13]; cnt++ )
{
/* Check block list element size */
if( *block & 0x80 )
{
/* 2-byte Block List element */
blocknb[cnt] = *(block + 1);
block += 2;
}
else
{
/* 3-byte Block List element */
blocknb[cnt] = *(block + 1);
blocknb[cnt] <<= 8;
blocknb[cnt] |= *(block + 2);
block += 3;
}
/* Return error if Blocknb > NmaxB */
nbmax = InformationBlock[3];
nbmax = (nbmax << 8) | InformationBlock[4];
if( blocknb[cnt] > nbmax )
{
rspData[idx - 3] = 0xFF;
rspData[idx - 2] = 0x70;
return (idx - 1);
}
}
for( cnt = 0; cnt < cmdData[13]; cnt++ )
{
if( blocknb[cnt] == 0x0000 )
{
/* Read information block */
ST_MEMCPY( &rspData[idx], InformationBlock, sizeof(InformationBlock));
idx += sizeof(InformationBlock);
}
else
{
/* Read ndef block */
ST_MEMCPY( &rspData[idx], &ndefFile[2 + ((blocknb[cnt] - 1) * T3T_BLOCK_SIZE) ], T3T_BLOCK_SIZE);
idx += T3T_BLOCK_SIZE;
}
}
return idx;
}
/**
*****************************************************************************
* @brief Manage the T3T Update answer to the reader
*
* @param[in] cmdData : pointer to the received command.
* @param[in] rspData : pointer to the answer to send.
*
* @return Answer size.
*****************************************************************************
*/
static uint16_t demoCeT3TUpdate(uint8_t *cmdData, uint8_t *rspData)
{
/*
* Cmd: cmd | NFCID2 | NoS | Service code list | NoB | Block list | Block Data
* Rsp: rsp | NFCID2 | Status Flag 1 | Status Flag 2
*/
uint8_t *block;
uint16_t blocknb[256];
uint32_t idx = 0;
uint32_t cnt = 0;
uint32_t nbmax = 0;
/* Command response */
rspData[idx++] = RFAL_NFCF_CMD_WRITE_WITHOUT_ENCRYPTION + 1;
/* NFCID 2 bytes */
if( ST_BYTECMP( gNfcfNfcid, &cmdData[ RFAL_NFCF_LENGTH_LEN + RFAL_NFCF_CMD_LEN ], RFAL_NFCF_NFCID2_LEN ) == 0 )
{
ST_MEMCPY( &rspData[idx], gNfcfNfcid, RFAL_NFCF_NFCID2_LEN );
idx += RFAL_NFCF_NFCID2_LEN;
}
else
{
/* If NFCID2 in command is different, no answer */
return 0;
}
/* Check for command errors */
if( (cmdData[10] != 1) || (cmdData[11] != 0x09)
|| (cmdData[13] == 0) || (cmdData[13] > InformationBlock[2]) )
{
rspData[idx++] = 0xFF;
rspData[idx++] = 0xFF;
return idx;
}
else
{
rspData[idx++] = 0x00;
rspData[idx++] = 0x00;
}
/* Retrieving block to read */
block = &cmdData[14];
for( cnt = 0; cnt < cmdData[13]; cnt++ )
{
/* Check block list element size */
if( *block & 0x80 )
{
/* 2-byte Block List element */
blocknb[cnt] = *(block + 1);
block += 2;
}
else
{
/* 3-byte Block List element */
blocknb[cnt] = *(block + 1);
blocknb[cnt] <<= 8;
blocknb[cnt] |= *(block + 2);
block += 3;
}
/* Return error if Blocknb > NmaxB */
nbmax = InformationBlock[3];
nbmax = (nbmax << 8) | InformationBlock[4];
if( blocknb[cnt] > nbmax )
{
rspData[idx - 2] = 0xFF;
rspData[idx - 1] = 0x70;
return idx;
}
}
for( cnt = 0; cnt < cmdData[13]; cnt++ )
{
if( blocknb[cnt] == 0x0000 )
{
/* Write information block */
ST_MEMCPY( InformationBlock, block, T3T_BLOCK_SIZE);
block += T3T_BLOCK_SIZE;
}
else
{
/* Read ndef block */
ST_MEMCPY( &ndefFile[2 + ((blocknb[cnt] - 1) * T3T_BLOCK_SIZE) ], block, T3T_BLOCK_SIZE);
block += T3T_BLOCK_SIZE;
}
}
/* Status flag answer */
rspData[idx++] = 0x00;
rspData[idx++] = 0x00;
return idx;
}
/**
*****************************************************************************
* @brief Demo CE Initialize
*
* Initializes the demo CE
*
* @param[in] nfcfNfcid : The NFCID to be used in T3T CE.
*
* @return None
*****************************************************************************
*/
void demoCeInit( uint8_t* nfcfNfcid )
{
ST_MEMCPY(gNfcfNfcid, nfcfNfcid, RFAL_NFCF_NFCID2_LEN );
ST_MEMCPY(ndefFile, (uint8_t *)ndef_uri, sizeof(ndef_uri) );
}
/**
*****************************************************************************
* @brief Demo CE T3T
*
* Parses the received command and computes the response to be sent back to
* the Reader
*
* @param[in] rxData : pointer to the received command.
* @param[in] rxDataLen : length of the received command.
* @param[in] txBuf : pointer to where the response will be placed.
* @param[in] txBufLen : size of txBuf.
*
* @return Response size.
*****************************************************************************
*/
uint16_t demoCeT4T(uint8_t *rxData, uint16_t rxDataLen, uint8_t *txBuf, uint16_t txBufLen )
{
if( (rxData == NULL) || (rxDataLen < 4) || (txBuf == NULL) || (txBufLen < 2) )
{
return 0;
}
if(rxData[0] == T4T_CLA_00)
{
switch(rxData[1])
{
case T4T_INS_SELECT:
return demoCeT4TSelect(rxData, txBuf);
case T4T_INS_READ:
return demoCeT4TRead(rxData, txBuf);
case T4T_INS_UPDATE:
return demoCeT4TUpdate(rxData, txBuf);
default:
break;
}
}
/* Function not supported ... */
txBuf[0] = ((char)0x68);
txBuf[1] = ((char)0x00);
return 2;
}
/**
*****************************************************************************
* @brief Demo CE T3T
*
* Parses the received command and computes the response to be sent back to
* the Reader
*
* @param[in] rxData : pointer to the received command.
* @param[in] rxDataLen : length of the received command.
* @param[in] txBuf : pointer to where the response will be placed.
* @param[in] txBufLen : size of txBuf.
*
* @return Response size.
*****************************************************************************
*/
uint16_t demoCeT3T(uint8_t *rxData, uint16_t rxDataLen, uint8_t *txBuf, uint16_t txBufLen )
{
if( (rxData == NULL) || (rxDataLen < 4) || (txBuf == NULL) || (txBufLen < 2) )
{
return 0;
}
switch(rxData[1])
{
case RFAL_NFCF_CMD_READ_WITHOUT_ENCRYPTION:
return demoCeT3TCheck(rxData, txBuf);
case RFAL_NFCF_CMD_WRITE_WITHOUT_ENCRYPTION:
return demoCeT3TUpdate(rxData, txBuf);
default:
break;
}
/* Function not supported ... */
txBuf[0] = ((char)0xFF);
txBuf[1] = ((char)0xFF);
return 2;
}
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/