当前位置:   article > 正文

NRF52832实现Mifare K1门禁卡模拟的探索_nrf52832 nfc

nrf52832 nfc

      由于产品规划的因素,想实现NRF52832单片机模拟NFC门禁卡的功能;我通过这段时间查找资料和学习,对NFC实现门禁卡需要做的工作,以及其中的难点,做如下总结。
一、NFC的协议分层、卡类型
      先上一张图,这张图可以清楚看到NFC的协议结构,以及NFC支持的卡类型:
        图片上标注灰色的部分是标准协议,常用的就是ISO1443A,  在之上不同的厂家会实现各自独特的通讯协议和加密验证算法。从这张图可以看到,NXP的卡的种类比较多。
二、MIFARE 1K 卡
     目前主流的门禁卡还是M1卡,M1卡是由NXP公司研发的门禁卡,目前是市场上主流的门卡;它的存储空间由1K,分为16个扇区,每个扇区有64个字节,每个扇区分为4个块,每块16个字节。
      
       如下图所示:
        第0扇区的第0块,保存卡UID和厂家信息,可读不可写,其它的扇区和块一般可读可写;每个扇区的最后一块保存每个扇区单独的密钥以及访问控制字。
        读卡器能够正确的从M1卡中读取数据,需要经历如下步骤:读卡器发起请求、选卡、认证、读写数据,下列的图片展示了读取数据的流程:
        
、MIFARE 1K 卡 的加密和认证
       M1卡采用一种叫做Crypto1的加密算法进行加密,这是一种流加密算法,在github上搜索就能找到源码。最初设计此算法的是NXP公司,此算法不开源。
  1. 当 MIFARE Classic 卡接近读卡器的磁场区域时,卡片会接收到读卡器发来的寻卡指令,然后按照防冲突协议发出自己的卡号UID(寻卡过程)。
  2. 收到UID 后,读卡器会选择这张卡(选卡过程)。
  3. 接着读卡器发出对某一块的认证请求,然后就开始了一个标准的三步认证协议(认证过程)。
  4. 卡片产生一个随机数nT 并以明文方式发送给读卡器。
  5. 紧接着读卡器发出它的随机数nR和对卡片的应答aR
  6. 最后卡片返回一个对读卡器的应答aT认证完成

      选卡的过程和放冲突模式是硬件自动操作的,所以从第三步开始的协议解析、身份认证、数据加密,才是工作难点。

标签对读卡器的认证

  1. crypto1_init(&state, (uint64_t)tc[k].key); //通过密钥初始化
  2. crypto1_word(&state, tc[k].uid ^ tc[k].tag_challenge, 0);//输入UID和标签的随机数
  3. crypto1_word(&state, tc[k].reader_challenge_enc, 1);//读卡器发送的随机数
  4. rresp = prng_successor(tc[k].tag_challenge, 64);//验证标签的随机数和
  5. rresp ^= crypto1_word (&state, 0, 0);
  6. if(rresp == tc[k].reader_response)//验证读卡器的随机数是否和标签发送的数据数一样   
  7.      printf("TAG> Reader is authentic.\n");
  8. else
  9.      printf("TAG> Reader is NOT authentic.\n");

读卡器对标签的认证

  1. crypto1_init(&state, tc[k].key);
  2. crypto1_word(&state, tc[k].uid ^ tc[k].tag_challenge, 0);
  3. rchal = crypto1_word(&state, tc[k].reader_challenge, 0);
  4. rresp = prng_successor(tc[k].tag_challenge, 64);
  5. rresp ^= crypto1_word (&state, 0, 0);
  6. tresp = prng_successor(tc[k].tag_challenge, 96);
  7. tresp ^= crypto1_word (&state, 0, 0);
  8. if(tresp == tc[k].tag_response)//读卡器验证随机数
  9. printf("Reader> Tag is authentic.\n");
  10. else
  11. printf("Reader> Tag is NOT authentic.\n");

ISO14443 crc校验算法

  1. #define CRC_A 1
  2. #define CRC_B 2
  3. #define BYTE unsigned char
  4. unsigned short UpdateCrc(unsigned char ch, unsigned short *lpwCrc)
  5. {
  6. ch = (ch^(unsigned char)((*lpwCrc) & 0x00FF));
  7. ch = (ch^(ch<<4));
  8. *lpwCrc = (*lpwCrc >> 8)^((unsigned short)ch << 8)^((unsigned short)ch<<3)^((unsigned short)ch>>4);
  9. return(*lpwCrc);
  10. }
  11. void ComputeCrc(int CRCType, char *Data, int Length,BYTE *TransmitFirst, BYTE *TransmitSecond)
  12. {
  13. unsigned char chBlock;
  14. unsigned short wCrc;
  15. switch(CRCType) {
  16. case CRC_A:
  17. wCrc = 0x6363; // ITU-V.41
  18. break;
  19. case CRC_B:
  20. wCrc = 0xFFFF; // ISO 3309
  21. break;
  22. default:
  23. return;
  24. }
  25. do {
  26. chBlock = *Data++;
  27. UpdateCrc(chBlock, &wCrc);
  28. } while (--Length);
  29. if (CRCType == CRC_B)
  30. wCrc = ~wCrc; // ISO 3309
  31. *TransmitFirst = (BYTE) (wCrc & 0xFF);
  32. *TransmitSecond = (BYTE) ((wCrc >> 8) & 0xFF);
  33. return;
  34. }
  35. BYTE BuffCRC_A[] = { 0x53,0x45,0x47,0x47,0x45,0x52,0x20,0x52};
  36. BYTE BuffCRC_B[] = { 0xC8,0xF0,0xD8,0x2E,0x7B};
  37. unsigned short Crc;
  38. BYTE First, Second;
  39. int crc_main(void)
  40. {
  41. printf("CRC-16 reference results 3-Jun-1999\n");
  42. printf("by Mickey Cohen - mickey@softchip.com\n\n");
  43. printf("Crc-16 G(x) = x^16 + x^12 + x^5 + 1\n\n");
  44. printf("CRC_A of [ ");
  45. printf("%02X ",BuffCRC_A[i]);
  46. ComputeCrc(CRC_A, BuffCRC_A, sizeof(BuffCRC_A), &First, &Second);
  47. printf("] Transmitted: %02X then %02X.\n", First, Second);
  48. return(0);
  49. }

、使用NRF52832模拟M1门卡遇到的问题
      我在52832开源代码hal_nfc_tct上修改的,防冲突和选卡已经成功了,读卡器下发认证指令 60 00 F5 7B后, 标签回复4字节随机数+2个字节CRC,此时读卡器应该回复4字节的随机数+4字节的应答 +2字节的CRC的数据,一共10个字节,但是我只收到了读卡器的9个字节。数据流如下
      reader :  60 00 F5 7B 
      tag: 82 a4  16 6c 85 46
       reader :  1a 21 6a 40 f2 ef 52 ee 32(一共只有9个字节)
      如果最后这一步CRC校验成功了的话,才能开始标签对读卡器的认证;读卡器使其他的卡测试可以正常使用,我目前卡在这里:。
      目前可以排查方向如下:
            1.接收的数据中第一个字节丢失
            2.读卡器发送的数据有特殊的协议格式,我这边解析错误
       由于这两个方向都需要知道读卡器发送的数据,但是目前的读卡器是直接买的,我是不知道读卡器发送的具体数据,目前也没有什么好的方法。
  
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/223515
推荐阅读