美文网首页
RC663 基于NXP NFC reader 读取 FM1216

RC663 基于NXP NFC reader 读取 FM1216

作者: V1ki | 来源:发表于2019-05-16 11:52 被阅读0次

    前言

    最近由于项目需要使用 RC663 读取 FM1216 的 卡片,最初的项目是基于 S70(mifare) 来开发的,而且仅仅是对卡号进行了读取,并未对卡片的内容进行读写操作.而且加之使用的NFC Reader 库是比较老的版本,所以我就下载了新版本,开始了整(踩)合(坑)之路.

    1.NXP NFC reader library 的集成

    我使用的NFCLib 的版本为: NxpNfcRdLib_RC663_LPC1769_v05.21.00_Full

    首先是导入源文件 , 导入头文件 , 这部分就跳过了,因为都是重复操作,没有什么需要注意的,缺什么文件就导入什么文件.

    然后,因为我使用的是RC663 ,所以定义 宏 NXPBUILD__PHHAL_HW_RC663 .
    而且,我 定义了PHDRIVER_PIRC663_BOARD 用于 控制NSS 脚的拉高拉低.
    其次,我没有使用FreeRTOS .所以声明了 PH_OSAL_NULLOS

    在这些都弄好后, 就到了驱动层了, 我现在用的是SPI, 所以将 SPI 初始化以及读写部分的函数实现替换为自己的函数.
    并且将控制 POWER-DOWN 及 NSS 两个GPIO的 实现替换为自己的实现.

    中断部分目前我没有弄.

    2. NFC Reader library 的初始化

    1. 首先初始化NfcLib

    phStatus_t wStatus ;
    phNfcLib_Status_t     dwStatus;
    
    phNfcLib_AppContext_t AppContext;
    phbalReg_Type_t sBalParams;
    
    wStatus = phbalReg_Init(&sBalParams, sizeof(phbalReg_Type_t));
    
    AppContext.pBalDataparams = &sBalParams ;
    
    dwStatus = phNfcLib_SetContext(&AppContext);
    
    dwStatus = phNfcLib_Init();
    
    

    2. 初始化 14443 协议层

    phhalHw_Rc663_DataParams_t    * pHal;
    void *pspalI14443p3a;
    void *pspalI14443p3b;
    void *pspalI14443p4a;
    void *pspalI14443p4;
    
    pHal = phNfcLib_GetDataParams(PH_COMP_HAL);
    pspalI14443p3a = phNfcLib_GetDataParams(PH_COMP_PAL_ISO14443P3A);
    pspalI14443p3b = phNfcLib_GetDataParams(PH_COMP_PAL_ISO14443P3B);
    pspalI14443p4a = phNfcLib_GetDataParams(PH_COMP_PAL_ISO14443P4A);
    pspalI14443p4 = phNfcLib_GetDataParams(PH_COMP_PAL_ISO14443P4);
    
    /* Configure HAL to ISO mode */
    wStatus = phhalHw_SetConfig(
                  pHal,
                  PHHAL_HW_CONFIG_OPE_MODE,
                  RD_LIB_MODE_ISO);
    
    /* Configure I14443-A PAL to ISO mode */
    wStatus = phpalI14443p3a_SetConfig(
                  pspalI14443p3a,
                  PHPAL_I14443P3A_CONFIG_OPE_MODE,
                  RD_LIB_MODE_ISO);
    
    /* Configure I14443-B PAL to ISO mode */
    wStatus = phpalI14443p3b_SetConfig(
                  pspalI14443p3b,
                  PHPAL_I14443P3B_CONFIG_OPE_MODE,
                  RD_LIB_MODE_ISO);
    
    /* Configure I14443-4A PAL to ISO mode */
    wStatus = phpalI14443p4a_SetConfig(
                  pspalI14443p4a,
                  PHPAL_I14443P4A_CONFIG_OPE_MODE,
                  RD_LIB_MODE_ISO);
    
    /* Configure I14443-4 PAL to ISO mode */
    wStatus = phpalI14443p4_SetConfig(
                  pspalI14443p4,
                  PHPAL_I14443P4_CONFIG_OPE_MODE,
                  RD_LIB_MODE_ISO);
    
    
    

    到这里 NfcLib 以及14443 的协议层就已经初始化完成了. 接下来就是使用了.

    这里唯一需要注意的就是 phbalReg_Init 中的实现了,这部分的实现是根据自身所使用的接口来选择的,我用的是SPI,所以这里就是初始化SPI

    3. NFC Reader library 的使用

    1. 引用 14443 协议

    wStatus = phhalHw_ApplyProtocolSettings(pHal, PHHAL_HW_CARDTYPE_ISO14443A);
    

    这里因为我需要读取的是FM1216 的卡片,所以就 应用的是 14443A Type-A

    2. 打开 Field

    wStatus = phhalHw_FieldOn(pHal);
    

    3. 做一个简单的延时 , 可以确保Field 打开

    wStatus = phhalHw_Wait(pHal,PHHAL_HW_TIME_MILLISECONDS, 6);
    

    4. 激活14443A的卡.

    wStatus = phpalI14443p3a_ActivateCard(
                  pspalI14443p3a,
                  NULL,
                  0x00,
                  bUid,
                  &bLength,
                  &bSak,
                  &bMoreCardsAvailable);
    

    5. 判断是否 PICC (Proximity Card) 是否符合 ISO/IEC 14443-4

     if ( (0x08 == (bSak & 0x08)) &&  (0x20 == (bSak & 0x20)) )
    

    参考自 <ISO/IEC 14443-3 Part 3: Initialization and anticollision> 6.4.3.4 Coding of SAK(Select acknowledge) . Table 6.8 - Coding of SAK

    6. Send RATS

    wStatus = phpalI14443p4a_Rats(
                  pspalI14443p4a,
                  0,
                  bCid,
                  Ats);
    

    7. 获取 14443-4A 的参数并设置 14443-4 协议参数

    /* Get parameters from 4A */
    wStatus = phpalI14443p4a_GetProtocolParams(
                  pspalI14443p4a,
                  &bCidEnabled,
                  &bCid,
                  &bNadSupported,
                  &bFwi,
                  &bFsdi,
                  &bFsci);
    
    /* Apply parameters to layer 4 */
    wStatus = phpalI14443p4_SetProtocol(
                  pspalI14443p4,
                  bCidEnabled,
                  bCid,
                  0,
                  0,
                  bFwi,
                  bFsdi,
                  bFsci);
    
    

    8. 交换数据

    wStatus = phpalI14443p4_Exchange(
                  pspalI14443p4,
                  PH_EXCHANGE_DEFAULT,
                  pTxBuffer,
                  wTxLength,
                  ppRxBuffer,
                  pRxLength);
    

    9. 关闭Field

    wStatus = phhalHw_FieldOff(pHal);
    

    关于上述流程,参考自 <ISO/IEC 14443-4 Part 4: Transmission protocol> 5. Protocol activation of PICC Type A . Figure 1 (the activation sequence for a PICC type A in the view of a PCD)
    另外每完成一个步骤,应该先检查是否执行成功,如果执行成功,才继续往下走.

    4. FMCOS 的使用

    参考资料 :

    我使用的是FM1216系列CPU 卡 ,而他搭载的COS 正是 FMCOS . 这里我不得不吐槽下,FMCOS 的资料真的是好少...

    首先要执行发卡流程.
    1. 取随机数

    uint8_t CMD[] = {0x00, 0x84, 0x00, 0x00, 0x04};
    wStatus = phpalI14443p4_Exchange(
                  pspalI14443p4,
                  PH_EXCHANGE_DEFAULT,
                  CMD,
                  sizeof(CMD),
                  ppRxBuffer,
                  pRxLength);
    
    

    2. 当取随机数成功后, 返回状态 为 '90 00' 的时候.对齐进行DES 加密后,进行外部认证

    uint8_t ExternalAuthentiateData[13] = {0x00,0x82,0x00,0x00,0x08,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF} ;
    uint8_t Data[8] ;
    uint8_t EncryptData[8] ;
    uint8_t i ;
    
    memset(Data,0,sizeof(Data));
    memcpy(Data,Challenge,4);
    
    des(Data,ExternalKey,0,EncryptData);
    
    for(i=5; i<13; i++) {
        ExternalAuthentiateData[i] = EncryptData[i-5] ;
    }
    
    printBuf("ExternalAuthentiateData:", ExternalAuthentiateData, 13, 32);
    wStatus = phpalI14443p4_Exchange(
                  pspalI14443p4,
                  PH_EXCHANGE_DEFAULT,
                  ExternalAuthentiateData,
                  sizeof(ExternalAuthentiateData),
                  &pRxData,
                  &wRxDataLength);
    
    

    3. 卡片擦除

    uint8_t CMD[] = {0x80,0x0E,0x00,0x00,0x00} ;
    wStatus = phpalI14443p4_Exchange(
                  pspalI14443p4,
                  PH_EXCHANGE_DEFAULT,
                  CMD,
                  sizeof(CMD),
                  &pRxData,
                  &wRxDataLength);
    

    4. 选择MF

    uint8_t choose_file [] = {0x00,0xA4,0x00,0x00,0x02,0x3F,0x00};
    wStatus = phpalI14443p4_Exchange(
                  pspalI14443p4,
                  PH_EXCHANGE_DEFAULT,
                  choose_file,
                  sizeof(choose_file),
                  &pRxData,
                  &wRxDataLength);
    

    5. 创建密钥文件

    uint8_t CMD [20] = {0x80,0xE0,
                        0x00,0x00,  // 文件标识
                        0x07, // lc
                        0x3F,  // 文件类型
                        0x00,0xB0, // 文件空间
                        0x01,  //读权限
                        0xF0,  //写权限
                        0xFF,
                        0xFF  // 详细见6.13.3
                       };
    wStatus = phpalI14443p4_Exchange(
                  pspalI14443p4,
                  PH_EXCHANGE_DEFAULT,
                  CMD,
                  sizeof(CMD),
                  &pRxData,
                  &wRxDataLength);
    

    6. 添加 PIN 到密钥中

    uint8_t AddPin2KEY[18] = { 0x80,0xD4,
                               0x01,0x00, //文件标识
                               0x0D,  // 长度
                               0x39,  // 文件类型. 0x39为 外部认证密钥
                               0xF0,  // 读权限
                               0xF0,  // 写权限
                               0xAA,  // 后续状态
                               0x88,  // 错误计数 ,高字节为最多错误次数,低字节为还可以继续错误次数
                               0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF         //密钥
                             } ;
    wStatus = phpalI14443p4_Exchange(
                  pspalI14443p4,
                  PH_EXCHANGE_DEFAULT,
                  AddPin2KEY,
                  sizeof(AddPin2KEY),
                  &pRxData,
                  &wRxDataLength);
    
    

    7. 建立目录(DF)

    uint8_t CMD[29] = { 0x80, 0xE0, // 创建文件
                        (uint8_t)(FileID >> 8),(uint8_t)FileID,  // 文件标识
                        0x0D, // 文件长度
                        0x38,  // 文件类型
                        0x05, 0x20,  //  文件空间大小
                        0xF0,  // 读权限
                        0xF0,  // 写权限
                        0x95,  // 应用文件ID
                        0xFF, 0xFF,   // reversed
                        // DF 名称 ASCII 码 5- 16 个字节
                      };
    wStatus = phpalI14443p4_Exchange(
                  pspalI14443p4,
                  PH_EXCHANGE_DEFAULT,
                  AddPin2KEY,
                  sizeof(AddPin2KEY),
                  &pRxData,
                  &wRxDataLength);
    

    8. 选择DF(其实和选择MF 是一样的命令,只是 文件标识不同

    uint8_t choose_file [] = {0x00,0xA4,0x00,0x00,0x02,0x3F,0x01};
    wStatus = phpalI14443p4_Exchange(
                  pspalI14443p4,
                  PH_EXCHANGE_DEFAULT,
                  choose_file,
                  sizeof(choose_file),
                  &pRxData,
                  &wRxDataLength);
    

    9. 创建 DF 内的密钥 文件. 参考 5 创建密钥文件

    10. 添加 PIN 到 DF 内的密钥中. 参考 6 添加 PIN 到密钥中

    11. 建立二进制文件

    uint8_t CreateFile_EF_BIN [] = {0x80,0xE0,
                                    0x00,0x03,//File ID
                                    0x07, // lc
                                    0x28,  //文件类型. 
                                    0x00,0x1E, //  文件大小
                                    0xF0,  //读权限
                                    0xF0,  //写权限
                                    0xFF,
                                    0x02  // 详细见6.13.3
                                   };
    wStatus = phpalI14443p4_Exchange(
                  pspalI14443p4,
                  PH_EXCHANGE_DEFAULT,
                  CreateFile_EF_BIN,
                  sizeof(CreateFile_EF_BIN),
                  &pRxData,
                  &wRxDataLength);
    

    12. 选择二进制文件 ,参考 选择MF,只是 文件标识不同

    13. 写入数据到二进制文件 , 需要注意的是 数据大小不能超过文件空间的大小;

    uint8_t WriteFile_BIN [] = {0x00,0xD6,
                                0x00,0x00,//P1 , P2
                                0x05, // lc
                                0x01,0x02,0x03,0x02,0x01 //  写入的数据
                               };
    wStatus = phpalI14443p4_Exchange(
                  pspalI14443p4,
                  PH_EXCHANGE_DEFAULT,
                  WriteFile_BIN,
                  sizeof(WriteFile_BIN),
                  &pRxData,
                  &wRxDataLength);
    

    然后是读卡流程

    1. 选择DF ,参考 发卡流程的选择DF

    2. 验证口令

    uint8_t Verify_Pin [] = {0x00,0x20,
                             0x00,0x01,// 密钥标识
                             0x03, // lc
                             0x12,0x34,0x56// 密码
                            };
    wStatus = phpalI14443p4_Exchange(
                  pspalI14443p4,
                  PH_EXCHANGE_DEFAULT,
                  Verify_Pin,
                  sizeof(Verify_Pin),
                  &pRxData,
                  &wRxDataLength);                        
    
    

    3. 选择二进制文件 参考 发卡流程的选择二进制文件

    4. 读取二进制文件

    uint8_t ReadFile_BIN [] = {0x00,0xB0,
                               0x00,0x00,//P1 , P2 
                               0x00,0x00
                              };
    wStatus = phpalI14443p4_Exchange(
                  pspalI14443p4,
                  PH_EXCHANGE_DEFAULT,
                  ReadFile_BIN,
                  sizeof(ReadFile_BIN),
                  &pRxData,
                  &wRxDataLength);                        
    
    

    目前还没有弄循环记录文件 ,等弄好了之后再继续更新.

    相关文章

      网友评论

          本文标题:RC663 基于NXP NFC reader 读取 FM1216

          本文链接:https://www.haomeiwen.com/subject/cqobaqtx.html