NXP

Android程序运行分析——中等复杂程度的NTAG I2C Demo为例(六)

2019-07-12 12:58发布

本文针对NT3H2111芯片DataSheet中有关Authenticate的内容进行整理汇总,用一种比较好理解的方式叙述 DataSheet下载链接:https://www.nxp.com/docs/en/data-sheet/NT3H2111_2211.pdf
NFC技术的一个重要应用场景是,移动支付领域,因此相关的安全技术是必不可少的 对于这种安全认证技术,NXP公司的这款NT3H2111芯片,自然有很多优化的措施,本文介绍NXP这款产品是如何在芯片的层面完成密码认证的机制的 下图是NFC角度的NT3H2111芯片的memory组织方式 可以看到,从NFC的角度,芯片的第一层框架为sector,主要的东西都放在第一个sector里面;第二层框架是page,每一个sector有255个page,;第三层框架是Byte,也就是字节,一个Page有4个字节 下面描述一下整个认证的过程: 首先,一开始的时候密码保护状态肯定是禁用的,这个时候请关注一下AUTH0这个位置,其实本质上AUTH0就是一个位于Sector0并且Page地址在E3h的第4个Byte(从0开始数是第4个)的那个Byte AUTH0的作用是,指定在SECTOR0中,被保护的区域是从哪里开始的,也就是被保护区域的起始地址。当然若AUTH0里的值大于EBh,则密码保护会被禁用 关于AUTH0,有一句话我还没看明白,先挖个坑 从NFC的视角来看,有一块区域是专门用于实现这个密码认证的,如图所示,从E3H到E7H 如果从I2C的角度来看,是block从38H到39H 当然这款区域是在EEPROM里的(SRAM相当于内存,掉电就没了,显然这些重要的东西不应该放在SRAM里面) 我们已经知道,这款NFC芯片的memory(不知道应该怎么翻译)根据存储体性质的不同,可以分为2大块,即EEPROM和SRAM,那么,从某种意义上,我们可以把这个芯片作如下划分:
  • SRAM
  • 受保护的EEPROM(就是上面提到的)
  • 不受保护的EEPROM
当然受保护的EEPROM容量会小一些,不受保护的EEPROM容量大一些
 下面就开始说存放密码的区域了,也就是PWD区域
  • PWD,用于存放32位的密码
  • PACK,用于存放16位的Ack
显然这两个区域你是不可能通过手机去读的,DataSheet里面也写的很清楚 就是说你要是去读取这两个区域,读取的结果只能是返回全0。当然写是可以的,也就是刚刚上面提到的

 PT_I2C

这其实是一个Byte的简称,也就是那个位于SECTOR0的Page地址为E7H的第0个Byte,我们给他取名字叫做PT_I2C PT_I2C里面的8位是这样的 这里主要关注一下SRAM_PORT这一位(如果我们要给某一bit取一个别名,就在名字后面加上_PORT这样,显然,SRAM_PORT就是和SRAM的控制相关的一个bit)
  • 0     不需要pass through模式的密码认证
  • 1     需要pass through模式的密码认证
当然了,从I2C角度来说,我们始终有这样的权限去读写:
  • 会话寄存器
  • SRAM
  • 包括PWD配置区域的配置页

PWD_AUTH

假设你现在手上有一个带有NFC功能的手机,接下来是如何实现认证的呢? 这里需要介绍一条非常重要的指令,也就是PWD_AUTH,如图所示 这条指令带一个参数,也就是你的输入密码 这样看太抽象了,我们不如看一下源代码,这是在发送指令 有意思的是,这个函数在整个Project中只用了一次,就是用在这个地方 authenticatePlus这个函数相当于在pwdAuth的基础上再封装了一层 那么哪些地方用到了authenticatePlus呢 很有意思的事情,我要去认证,我当然要先判断我自己现在的权限状态,那么如何去看自己的状态呢,居然是用一个static的变量,因此我对这个变量能否及时更新有所质疑 Android客户端中所有对于mAuthStatus的更新,都是通过这个函数获得的。mAuthStatus我个人认为你应该将他理解成为NXP芯片中的某个变量的影射,而不是一个Android客户端中的一个static变量! 显然这个的具体实现就是这个了,先挖坑,以后再说 @Override public int getProtectionPlus() { try { reader.SectorSelect((byte) 0); byte[] auth0 = getAuth0Register(); if(auth0 != null && auth0.length < 4) { try { readSRAMBlock(); return AuthStatus.Protected_RW.getValue(); } catch (IOException e) { e.printStackTrace(); } catch (FormatException e) { e.printStackTrace(); } return AuthStatus.Protected_RW_SRAM.getValue(); } else { if((auth0[3] & 0xFF) <= 0xEB) { byte[] access = getAccessRegister(); byte[] pti2c = getPTI2CRegister(); if (((0x0000080 & access[0]) >> Access_Offset.NFC_PROT.getValue() == 1) && ((0x0000004 & pti2c[0]) >> PT_I2C_Offset.SRAM_PROT.getValue() == 1)) { return AuthStatus.Protected_RW_SRAM.getValue(); } else if (((0x0000080 & access[0]) >> Access_Offset.NFC_PROT.getValue() == 1) && ((0x0000004 & pti2c[0]) >> PT_I2C_Offset.SRAM_PROT.getValue() == 0)) { return AuthStatus.Protected_RW.getValue(); } else if (((0x0000080 & access[0]) >> Access_Offset.NFC_PROT.getValue() == 0) && ((0x0000004 & pti2c[0]) >> PT_I2C_Offset.SRAM_PROT.getValue() == 1)) { return AuthStatus.Protected_W_SRAM.getValue(); } else if (((0x0000080 & access[0]) >> Access_Offset.NFC_PROT.getValue() == 0) && ((0x0000004 & pti2c[0]) >> PT_I2C_Offset.SRAM_PROT.getValue() == 0)) { return AuthStatus.Protected_W.getValue(); } } } return AuthStatus.Unprotected.getValue(); } catch (IOException e) { e.printStackTrace(); } catch (FormatException e) { e.printStackTrace(); } catch (CommandNotSupportedException e) { e.printStackTrace(); } // Check if the SRAM is lock try { readSRAMBlock(); return AuthStatus.Protected_RW.getValue(); } catch (IOException e) { e.printStackTrace(); } catch (FormatException e) { e.printStackTrace(); } return AuthStatus.Protected_RW_SRAM.getValue(); }

AUTHLIM

当然,为了防止暴力破解,用AuthLIM来表示最多可以尝试的次数,当开启后,他会自动记录下你的输入错误密码的次数,这个很简单,不多说了