ATECC508A芯片开发笔记(三):获取508A串号、随机数源码及I2C抓包分析
2017-07-16 18:25
537 查看
ATECC508A芯片开发笔记(三):获取508A串号、随机数源码及I2C抓包分析
APP层函数编写及源码分析At508_GetSernum()
GetSernum抓包记录
At508_GetRandom( )
GetRandom抓包记录
经过前两章节,CryptoAuthlib库已经成功适配在代码中了,下面简单测试一下API函数,并分析下I2C通讯流程:
APP层函数编写及源码分析
利用lib/atca_basic.c中的函数,简单封装成业务所需API1、`At508_GetSernum( uint8_t *sernum )
函数功能比较简单,只是读取508A串号,并打印出来:其中SerialNum是全局变量,用来存储串号(9byte);
atcab_init,在经常调用508情况下,只需要init一次,并不用
release,
ATCAIfaceCfg *gCfg = &cfg_ateccx08a_i2c_default; uint8_t SerialNum[ATCA_SERIAL_NUM_SIZE]; ATCA_STATUS At508_GetSernum( uint8_t *sernum ) { ATCA_STATUS status; atcab_init( gCfg ); status = atcab_read_serial_number( sernum ); atcab_release(); char displayStr[ATCA_SERIAL_NUM_SIZE*3]; int displen = sizeof(displayStr); atcab_bin2hex(sernum,ATCA_SERIAL_NUM_SIZE,displayStr,&displen); atcab_printbin_label((const uint8_t*)"\r\n Serial Number from atcab is: ",sernum, ATCA_SERIAL_NUM_SIZE); return status; }
而结构体指针gCfg 是ATCAIfaceCfg 类型,其实就是前几节所说的Iface对象,定义如下:
typedef struct { ATCAIfaceType iface_type; // active iface - how to interpret the union below ATCADeviceType devtype; // explicit device type union { // each instance of an iface cfg defines a single type of interface struct ATCAI2C { uint8_t slave_address; // 8-bit slave address uint8_t bus; 4000 // logical i2c bus number, 0-based - HAL will map this to a pin pair for SDA SCL uint32_t baud; // typically 400000 } atcai2c; struct ATCASWI { uint8_t bus; // logical SWI bus - HAL will map this to a pin or uart port } atcaswi; struct ATCAUART { int port; // logic port number uint32_t baud; // typically 115200 uint8_t wordsize; // usually 8 uint8_t parity; // 0 == even, 1 == odd, 2 == none uint8_t stopbits; // 0,1,2 } atcauart; struct ATCAHID { int idx; // HID enumeration index uint32_t vid; // Vendor ID of kit (0x03EB for CK101) uint32_t pid; // Product ID of kit (0x2312 for CK101) uint32_t packetsize; // Size of the USB packet uint8_t guid[16]; // The GUID for this HID device } atcahid; }; uint16_t wake_delay; // microseconds of tWHI + tWLO which varies based on chip type int rx_retries; // the number of retries to attempt for receiving bytes void *cfg_data; // opaque data used by HAL in device discovery } ATCAIfaceCfg;
gCfg 被初始化为:cfg_ateccx08a_i2c_default,既默认的508 I2C配置。可以看出,508芯片默认I2C地址都是0xC0(默认地址存在ConfigZone,可更改),波特率为400k。
ATCAIfaceCfg cfg_ateccx08a_i2c_default = { .iface_type = ATCA_I2C_IFACE, .devtype = ATECC508A, .atcai2c.slave_address = 0xC0, .atcai2c.bus = 2, .atcai2c.baud = 400000, //.atcai2c.baud = 100000, .wake_delay = 800, .rx_retries = 20 };
可以看出,At508_GetSernum是调用了508API:
atcab_read_serial_number()函数实现了读取串号,其实现如下:
ATCA_STATUS atcab_read_serial_number(uint8_t* serial_number) { // read config zone bytes 0-3 and 4-7, concatenate the two bits into serial_number uint8_t status = ATCA_GEN_FAIL; uint8_t bytes_read[ATCA_BLOCK_SIZE]; uint8_t block = 0; uint8_t cpyIndex = 0; uint8_t offset = 0; do { memset(serial_number, 0x00, ATCA_SERIAL_NUM_SIZE); // Read first 32 byte block. Copy the bytes into the config_data buffer block = 0; offset = 0; if ( (status = atcab_read_zone(ATCA_ZONE_CONFIG, 0, block, offset, bytes_read, ATCA_WORD_SIZE)) != ATCA_SUCCESS ) break; memcpy(&serial_number[cpyIndex], bytes_read, ATCA_WORD_SIZE); cpyIndex += ATCA_WORD_SIZE; block = 0; offset = 2; if ( (status = atcab_read_zone(ATCA_ZONE_CONFIG, 0, block, offset, bytes_read, ATCA_WORD_SIZE)) != ATCA_SUCCESS ) break; memcpy(&serial_number[cpyIndex], bytes_read, ATCA_WORD_SIZE); _atcab_exit(); return status; }
在
atcab_read_serial_number()中,其实是通过
atcab_read_zone()读取了508A芯片中配置区所存储的串号,然后返回而已。
atcab_read_zone()里面,其实就是实现了508A Read命令消息封装,以及发送。如下代码:
ATCA_STATUS atcab_read_zone(uint8_t zone, uint8_t slot, uint8_t block, uint8_t offset, uint8_t *data, uint8_t len) { ATCA_STATUS status = ATCA_SUCCESS; ATCAPacket packet; uint16_t addr; uint16_t execution_time = 0; do { // Check the input parameters if (data == NULL) return ATCA_BAD_PARAM; if ( len != 4 && len != 32 ) return ATCA_BAD_PARAM; // The get address function checks the remaining variables if ( (status = atcab_get_addr(zone, slot, block, offset, &addr)) != ATCA_SUCCESS ) break; // If there are 32 bytes to write, then xor the bit into the mode if (len == ATCA_BLOCK_SIZE) zone = zone | ATCA_ZONE_READWRITE_32; // build a read command packet.param1 = zone; packet.param2 = addr; if ( (status = atRead( _gCommandObj, &packet )) != ATCA_SUCCESS ) break; execution_time = atGetExecTime( _gCommandObj, CMD_READMEM); if ( (status = atcab_wakeup()) != ATCA_SUCCESS ) break; // send the command if ( (status = atsend( _gIface, (uint8_t*)&packet, packet.txsize )) != ATCA_SUCCESS ) break; // delay the appropriate amount of time for command to execute atca_delay_ms(execution_time); // receive the response if ( (status = atreceive( _gIface, packet.data, &(packet.rxsize) )) != ATCA_SUCCESS ) break; // Check response size if (packet.rxsize < 4) { if (packet.rxsize > 0) status = ATCA_RX_FAIL; else status = ATCA_RX_NO_RESPONSE; break; } if ( (status = isATCAError(packet.data)) != ATCA_SUCCESS ) break; memcpy( data, &packet.data[1], len ); } while (0); _atcab_exit(); return status; }
atcab_read_zone()首先会根据传入参数(所读取的zone、slot),通过
atRead组建
read命令,存储在packet中;之后通过
atcab_wakeup()命令唤醒508A,也起到了查找设备的作用,
atcab_wakeup()会调用atwake(),最终还是通过Hal层的
hal_i2c_wake()函数,执行wake操作。
ATCA_STATUS hal_i2c_wake(ATCAIface iface) { ATCAIfaceCfg *cfg = atgetifacecfg(iface); ATCA_STATUS status = ATCA_WAKE_FAILED; int bus = cfg->atcai2c.bus; uint8_t response[4] = { 0x00, 0x00, 0x00, 0x00 }; uint8_t expected_response[4] = { 0x04, 0x11, 0x33, 0x43 }; i2c_set_pin(i2c_hal_data[bus]->pin_sda, i2c_hal_data[bus]->pin_scl); i2c_send_wake_token(); atca_delay_us(cfg->wake_delay); uint16_t len = 4; //! Receive Wake Response //status = hal_i2c_receive(iface, response, sizeof(response)); status = hal_i2c_receive(iface, response, &len); if (status == ATCA_SUCCESS) { //! Compare response with expected_response if (memcmp(response, expected_response, 4) != 0) status = ATCA_WAKE_FAILED; } return status; }
hal_i2c_wake()中,通过I2C驱动向设备发送了一串为0的数,而根据508A Datasheet,508A接受到00会返回一条固定的数据:0x04, 0x11, 0x33, 0x43 ,因此
hal_i2c_wake()就是发送00然后接受数据,判断是否为上述数据,如果是,则唤醒成功。。同理,
atcab_read_zone()在wakeup后,通过
atsend()发送组装好的packet数据,也是同样的流程,只不过底层调用的是
hal_i2c_send()罢了。
–这些步骤也对应了上一节的API访问底层I2C驱动的逻辑结构关系。
GetSernum抓包记录:
通过逻辑分析仪连接到MCU与508A通讯的I2C总线上,执行At508_GetSernum()就可以看到如下抓包记录:(遵循Datasheet命令格式)I2C发送唤醒:MCU向0xC0地址设备发送wakeup数据帧,(图片看不清可右键在新页面打开观看。)
I2C接收唤醒:508A返回的唤醒相应消息
I2C发送:获取串号SerialNum命令:
I2C接收串号:508A返回串号数据(该芯片串号为 0123….)
总体效果图:
2、At508_GetRandom( uint8_t *randomnum)
与GetSN类似,GetRandom调用了atcab_random()实现随机数获取,其中RandomNum是一个508A返回的32Byte随机数的全局变量,
atcab_bin2hex只是打印输出需要。
uint8_t RandomNum[RANDOM_RSP_SIZE]; ATCA_STATUS At508_GetRandom( uint8_t *randomnum) { ATCA_STATUS status = ATCA_GEN_FAIL; char displayStr[RANDOM_RSP_SIZE*3]; int displen = sizeof(displayStr); atcab_init( gCfg ); status = atcab_random( randomnum ); atcab_release(); atcab_bin2hex(randomnum,32,displayStr,&displen); atcab_printbin_label((const uint8_t*)"\r\n Random Number is: ",randomnum, RANDOM_RSP_SIZE); return status; }
官方API函数
atcab_random()与上面
atcab_read_zone()如出一辙,也是先
wakeup,之后
atsend发送组装的命令,然后
atreceive等待接收508返回的数据。
ATCA_STATUS atcab_random(uint8_t *rand_out) { ATCA_STATUS status = ATCA_GEN_FAIL; ATCAPacket packet; uint16_t execution_time = 0; if ( !_gDevice ) return ATCA_GEN_FAIL; // build an random command packet.param1 = RANDOM_SEED_UPDATE; //packet.param1 = RANDOM_NO_SEED_UPDATE; packet.param2 = 0x0000; status = atRandom( _gCommandObj, &packet ); execution_time = atGetExecTime( _gCommandObj, CMD_RANDOM); do { if ( (status = atcab_wakeup()) != ATCA_SUCCESS ) break; // send the command if ( (status = atsend( _gIface, (uint8_t*)&packet, packet.txsize )) != ATCA_SUCCESS) break; // delay the appropriate amount of time for command to execute atca_delay_ms(execution_time); // receive the response if ( (status = atreceive( _gIface, packet.data, &packet.rxsize)) != ATCA_SUCCESS) break; // Check response size if (packet.rxsize < 4) { if (packet.rxsize b7bf > 0) status = ATCA_RX_FAIL; else status = ATCA_RX_NO_RESPONSE; break; } if ( (status = isATCAError(packet.data)) != ATCA_SUCCESS ) break; memcpy( rand_out, &packet.data[1], 32 ); // data[0] is the length byte of the response } while (0); _atcab_exit(); return status; }
GetRandom抓包记录:
–>wakeup省略。以下是508A返回:注意:508A拿到手是需要先手动config,并且Lock的(官方叫做Provision)。如果不进行该步骤,既没有Lock配置区,很多功能是不能使用,例如GetRandom,如果ConfigZone和DataZone没有被Lock,就会返回FFFF0000FFFF00….只有Lock后才能正确产生随机数。
–>然而ConfigZone Lock后就是一次性的,无法再解锁,所以下节会介绍如何根据需求正确配置508的ConfigZone以及DataZOne,既如何实现Provison流程。
我之前记录的读写508 某个Slot的数据,GetSN、GetRandom的截图(数据只是测试所用):
(slot是508A存储的一个单元区,可以放密钥、证书等其他数据,下节会介绍)欢迎转载,Howie原创作品,本文地址:
http://write.blog.csdn.net/mdeditor#!postId=75208753谢谢
相关文章推荐
- ATECC508A芯片开发笔记(六):产生CSR以及申请证书(X.509)流程及其内容分析
- ATECC508A芯片开发笔记(五):Provision执行过程及代码分析
- ATECC508A芯片开发笔记(二):开发准备之 CryptoAuthLib 库简介与移植
- [置顶] ATECC508A芯片开发笔记(一):初识加密芯片
- 网络视频源地址抓包分析(3)之获取腾讯视频源码
- Clamav杀毒软件源码分析笔记[三]
- 第二人生的源码分析(四十六)获取纹理图片的线程
- 第二人生的源码分析(四十六)获取纹理图片的线程
- 蔡军生先生第二人生的源码分析(四十六)获取纹理图片的线程
- ObjectARX.NET C#开发笔记(二):AcCtrlClient示例分析
- 第二人生的源码分析(七十八)LLOSInfo类实现获取操作系统信息
- Java源码分析:深入探讨Iterator模式-Java基础-Java-编程开发
- 蔡军生先生第二人生的源码分析(七十七)CProcessor类实现获取CPU信息
- 第二人生的源码分析(九十五)LLCheckBoxCtrl类实现复选按钮 - 大坡3D软件开发 - CSDNBlog
- Clamav杀毒软件源码分析笔记[二]
- Clamav杀毒软件源码分析笔记[四]
- 第二人生的源码分析(三十六)获取消息包里每一个字段
- [转]我也玩PSP开发!(2)—— PSPSDK示例 源码分析
- 第二人生的源码分析(七十八)LLOSInfo类实现获取操作系统信息
- 【PHP TO ASP.NET 2.0开发系列课程(1):PHP与ASP.NET2.0之比较分析】笔记