OTP加密使用指南
GX8002固件加密方案,通常会使用到flash id,并将加密后的数据写入到OTP(One Time Programmable)区域。本文将介绍如何获取flash id、如何操作OTP区域以及加密解密app demo使用方法。
1. Flash获取唯一id指南
1.1. API接口
下面介绍下获取Flash id需要调用的接口
1.1.1. flash初始化接口
| /***************************
flash初始化
Returns:
int flash初始化是否成功
Return value:
0 成功
-1 失败
**************************/
int gx_spinor_flash_init(void)
|
1.1.2. flash获取UID接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | /***********************
获取flash Unique ID
Parameters:
@buf 缓存
@buf_len 缓存长度
@ret_len UID长度地址,长度单位字节
Returns:
int 获取flash UID是否成功
Return value:
0 成功
-1 失败
************************/
int gx_spinor_flash_getuid(
unsigned char *buf,
int buf_len,
int *ret_len
)
|
1.2. 代码示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | // 需要引用的头文件
#include <driver/gx_flash.h>
int flash_get_uid()
{
unsigned char buf[16];
int ret_len;
unsigned int temp;
gx_spinor_flash_init(); // flash初始化
gx_spinor_flash_getuid(buf, 16 , &ret_len); // 获取uid
printf("ret_len:%d\n", ret_len); // 打印长度
for (int j = 0; j < 16;j++) {
printf("%02X ", buf[j]);
}
printf("\n");
return 0;
}
|
1.3. 注意事项
- 在获取UID之前需要调用flash初始化接口
- 需要注意UID的长度
2. OTP区域读写使用说明
OTP区域在flash里面,并且不占用flash提供给用户的空间。使用官方烧录工具NCdownloader可以导出flash中的固件,但是无法将OTP区域的信息导出,因此这块区域经常用于存放加密信息。不同的flash型号可能会有不同的OTP区域数量和大小。
提醒
一旦OTP区域上锁了,OTP区域的信息就永久变为只读属性,不可擦写。
2.1. API接口
2.1.1. flash初始化接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | /**
* @brief flash初始化
*
* @param busnum spi总线号
* @param cs spi片选
* @param max_hz 支持最大的spi频率
* @param spi_mode spi工作模式
*
* @return GX_FLASH_DEV flash设备
* @retval NULL 设备初始化失败
* @retval 非NULL flash设备
*/
GX_FLASH_DEV *gx_spi_flash_probe(unsigned int busnum, unsigned int cs,
unsigned int max_hz, unsigned int spi_mode);
|
2.1.2. 获取OTP区域数量接口
| /**
* @brief 获取flash otp区域数量
*
* @param devp flash设备, 详细说明请参考 gxdocref GX_FLASH_DEV
* @param region 输出参数,otp区域数量
*
* @return int 调用是否成功
* @retval 0 成功
* @retval -1 失败
*/
int gx_spi_flash_otp_get_region(GX_FLASH_DEV *devp, unsigned int *region);
|
2.1.3. 获取OTP区域大小接口
| /**
* @brief 获取 otp 区域大小
*
* @param devp flash设备, 详细说明请参考 gxdocref GX_FLASH_DEV
* @param size otp 区域大小
*
* @return int 调用是否成功
* @retval 0 成功
* @retval -1 失败
*/
int gx_spi_flash_otp_get_region_size(GX_FLASH_DEV *devp, unsigned int *size);
|
2.1.4. 获取OTP区域上锁状态接口
| /**
* @brief 获取flash otp 区域锁状态
*
* @param devp flash设备, 详细说明请参考 gxdocref GX_FLASH_DEV
* @param data 输出参数,写保护状态,0 未锁住,1 锁住
*
* @return int 调用是否成功
* @retval 0 成功
* @retval -1 失败
*/
int gx_spi_flash_otp_status(GX_FLASH_DEV *devp, unsigned char *data);
|
2.1.5. 给OTP区域上锁接口
| /**
* @brief 锁住当前flash otp 区域
*
* @param devp flash设备, 详细说明请参考 gxdocref GX_FLASH_DEV
*
* @return int 是否锁flash otp 成功
* @retval 0 成功
* @retval -1 失败
*/
int gx_spi_flash_otp_lock(GX_FLASH_DEV *devp);
|
2.1.6. 读取OTP区域内容接口
1
2
3
4
5
6
7
8
9
10
11
12
13 | /**
* @brief 读当前flash otp 区域数据
*
* @param devp flash设备, 详细说明请参考 gxdocref GX_FLASH_DEV
* @param addr 读当前otp 区域的起始地址
* @param data 读缓存
* @param len 读flash otp的长度
*
* @return int 调用是否成功
* @retval 0 成功
* @retval -1 失败
*/
int gx_spi_flash_otp_read(GX_FLASH_DEV *devp, unsigned int addr, unsigned char *data, unsigned int len);
|
2.1.7. 擦除OTP区域内容接口
| /**
* @brief 擦除当前flash otp 区域数据
*
* @param devp flash设备
*
* @return int 调用是否成功
* @retval 0 成功
* @retval -1 失败
*/
int gx_spi_flash_otp_erase(GX_FLASH_DEV *devp);
|
2.1.8. 写OTP区域接口
1
2
3
4
5
6
7
8
9
10
11
12
13 | /**
* @brief 写当前flash otp 区域数据
*
* @param devp flash设备, 详细说明请参考 gxdocref GX_FLASH_DEV
* @param addr 写当前otp 区域的起始地址
* @param data 写缓存
* @param len 写flash otp的长度
*
* @return int 调用是否成功
* @retval 0 成功
* @retval -1 失败
*/
int gx_spi_flash_otp_write(GX_FLASH_DEV *devp, unsigned int addr, unsigned char *data, unsigned int len);
|
2.2. 代码示例
参考app/flash_otp_demo/flash_otp_demo.c,这个demo对flash的所有可用OTP区域进行了擦读写验证。在menuconfig的lvp application settings中选择Flash OTP Demo,退出保存,编译后烧录即可进行验证。

3. OTP加密解密流程
参考app/otp_encryption_demo,在menuconfig的lvp application settings中选择OTP Encryption Demo,或者直接使用参考配置*configs/release/nationalchip/grus_gx8002b_dev_1v_flash_otp_encryption_authorization.config*。这个app提供了加密和解密两套代码,通过menuconfig中选择不同的配置实现加密和解密功能,下面对OTP加密和解密流程做进一步说明。

3.1. OTP存储加密信息流程
首先要获取Flash UID然后对其加密,并写入到OTP区域,最后上锁完成加密

3.1.1. 代码解读
lvp_tws/app/otp_encryption_demo/otp_encryption_demo.c |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96 | static void otp_encryption(void)
{
GX_FLASH_DEV *flash = flash_test.dev;
unsigned char writebuf[512] = {0}, readbuf[512] = {0}, tmpbuf[512] = {0};
unsigned char status;
unsigned int otp_size;
int ret;
unsigned int j;
printf("flash type %s\n", gx_spi_flash_gettype(flash));
ret = gx_spi_flash_otp_get_region_size(flash, &otp_size);
if (ret != 0) {
printf("not support otp\n");
return;
}
printf("otp region size: %d\n", otp_size);
if (otp_size > 512) {
otp_size = 512;
}
ret = gx_spi_flash_otp_set_region(flash, OTP_REGION_NUMBER); // 设置要操作的OTP区域
if (ret != 0) {
printf("set otp region fail, err = %d\n", ret);
return;
}
/* get status */
gx_spi_flash_otp_status(flash, &status);
if (status == 0) {
printf("otp region %d unlock\n", OTP_REGION_NUMBER);
} else {
printf("otp region %d lock\n", OTP_REGION_NUMBER);
}
if(status == 0) // 若OTP区域为上锁状态则跳过操作,直接退出
{
printf("use otp region %d to store key\n", OTP_REGION_NUMBER);
/* erase */
printf(" erase otp region %d\n", OTP_REGION_NUMBER);
ret = gx_spi_flash_otp_erase(flash);
if (ret == 0) {
printf(" erase success, ret = %d\n", ret);
} else if(ret == -1) {
printf(" erase fail, ret = %d\n", ret);
return;
}
/* write */
printf(" write otp region %d\n", OTP_REGION_NUMBER);
memcpy(tmpbuf, uid_buf, uid_buf_len);
encrypt_data_process(tmpbuf, writebuf, uid_buf_len); // 对flash id进行加密,加密方法可以直接在这个接口里自定义
ret = gx_spi_flash_otp_write(flash, 0, writebuf, otp_size); // 将加密信息写入OTP区域
if (ret == -1) {
printf(" write fail!\n");
return;
}
printf("writebuf info :\n");
for(j = 0; j < otp_size; j++)
printf(" 0x%02X,", writebuf[j]);
printf("\n");
memset(readbuf, 0, otp_size);
ret = gx_spi_flash_otp_read(flash, 0, readbuf, otp_size);
if (ret == -1) {
printf(" read fail!\n");
return;
}
for (j = 0; j < otp_size; j++) {
if (readbuf[j] != writebuf[j]) {
printf(" read otp offset readbuf[%d] != 0x%02x\n", j, writebuf[j]);
printf(" write fail!\n");
return;
}
}
printf("write success! write_len = %d\n", ret);
#ifdef CONFIG_ENABLE_LOCK_OTP_REGION
gx_spi_flash_otp_lock(flash); // 对OTP区域上锁
gx_spi_flash_otp_status(flash, &status);
if (status == 0) {
printf("lock fail!\n");
} else {
printf("lock success!\n");
}
#endif
}
else
printf("otp region is locked, not support to write data to it\n");
return 0;
}
|
3.1.2. app编译
在Application Settings中选择write encrypted data to OTP region,退出保存后编译即可。

提醒
考虑到OTP区域一旦上锁了就不可再被擦写,这里将上锁功能独立出来控制,需要上锁功能的话将下面的Lock OTP region使能即可
3.2. 8002上电后从OTP读取信息解密流程
- 对于带有加密信息的GX8002芯片,上电后先从OTP区域读取加密信息,然后进行解密,再和本机的flash id进行对比,相同即校验成功,正常上电,否则不能工作。

3.2.1. 代码解读
lvp_tws/app/otp_encryption_demo/otp_encryption_demo.c |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 | static int otp_authorization(void)
{
GX_FLASH_DEV *flash = flash_test.dev;
unsigned char readbuf[512] = {0}, tmpbuf[512] = {0};
unsigned char status;
unsigned int otp_size;
int ret;
unsigned int j;
printf("flash type %s\n", gx_spi_flash_gettype(flash));
ret = gx_spi_flash_otp_get_region_size(flash, &otp_size);
if (ret != 0) {
printf("not support otp\n");
return -1;
}
printf("otp region size: %d\n", otp_size);
if (otp_size > 512) {
otp_size = 512;
}
ret = gx_spi_flash_otp_set_region(flash, OTP_REGION_NUMBER); // 设置要操作的OTP区域
if (ret != 0) {
printf("set otp region fail, err = %d\n", ret);
return -1;
}
else
printf("read otp region %d for authorization verification\n", OTP_REGION_NUMBER);
/* read */
memset(readbuf, 0, otp_size);
ret = gx_spi_flash_otp_read(flash, 0, readbuf, otp_size); // 将OTP区域的加密信息读取出来
if (ret == -1) {
printf(" read fail!\n");
return -1;
}
decrypt_data_process(readbuf, tmpbuf, uid_buf_len); // 对加密信息进行解密
printf("otp region decrypted info: \n");
for (j = 0; j < uid_buf_len; j++) {
printf(" 0x%02X,", tmpbuf[j]);
}
printf("\n");
ret = memcmp(tmpbuf, uid_buf, uid_buf_len); // 将解密后的信息和flash id进行比较
if(ret != 0)
{
printf("flash UID not match!\n");
return -1;
}
else
printf("flash UID match!\n");
return 0;
}
|
3.2.2. app编译
在Application Settings中选择decrypt the otp region data for verification,退出保存后编译即可。

3.3. app demo注意事项
- 支持配置要操作的OTP区域,修改下面高亮部分的宏。可以参考app/flash_otp_demo/flash_otp_demo.c调用gx_spi_flash_otp_get_region接口查询可用的OTP区域数量。
lvp_tws/app/otp_encryption_demo/otp_encryption_demo.c |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | #include <lvp_app.h>
#include <stdlib.h>
#include <lvp_buffer.h>
#include <utility/types.h>
#include <driver/gx_flash.h>
#include <autoconf.h>
#include <driver/gx_flash_common.h>
#include <string.h>
#define LOG_TAG "[OTP_ENCRYPT_AUTHORIZATION_APP]"
#define OTP_REGION_NUMBER 0 //要操作的OTP区域
//=================================================================================================
struct flash_test_info {
GX_FLASH_DEV *dev;
int flash_type;
u32 flash_size;
u32 flash_block_size;
u32 flash_page_size;
};
|