/** ****************************************************************************** * @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
* T4T_VNo : Indicates the Mapping Version
* MLe high : Max R-APDU size
* MLc high : Max C-APDU size
* NDEF FCI T: Indicates the NDEF-File_Ctrl_TLV
* NDEF FCI L: The length of the V-field
* NDEF FCI V1: NDEF File Identifier
* NDEF FCI V2: NDEF File size
* NDEF FCI V3: NDEF Read AC
* NDEF FCI V4: NDEF Write AC
*/ 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
* NDEF Header: MB,ME,SR,Well known Type
* NDEF type length
* NDEF payload length
* NDEF Type : URI
* NDEF URI abreviation field : http://www.
* NDEF URI string : st.com/st25r
* NDEF URI string : st.com/st25-demo
*/ 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
* Nbr : Indicates the number of blocks that can be read
* Nbw : Indicates the number of blocks that can be written
* NmaxB : Indicates the maximum number of blocks available for NDEF data
* WriteFlag : Indicates whether a previous NDEF write procedure has finished or not
* RWFlag : Indicates data can be updated or not
* Ln : Is the size of the actual stored NDEF data in bytes
* Checksum : allows the Reader/Writer to check whether the Attribute Data are correct
*/ 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****/