跳转至

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初始化接口*

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/***************************
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区域数量接口*

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/**
 * @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区域大小接口*

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/**
 * @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区域上锁状态接口*

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/**
 * @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区域上锁接口*

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/**
 * @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区域内容接口*

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/**
 * @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;
    };