前言
最近由于项目需要使用 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);
目前还没有弄循环记录文件 ,等弄好了之后再继续更新.