一 背景说明

​ 项目程序需要进行加密处理。

​ 考虑利用嵌入式芯片的唯一UID,结合Flash读写来实现。

​ 加密后的程序,可以使得从芯片Flash中读取出来的文件(一般为HEX格式)不能用于其他的芯片。

二 原理介绍

​ 【1】芯片唯一ID

​ 芯片提供了器件电子签名,即唯一身份标识。该信息存储在产品唯一身份标识寄存器中:

​ UID寄存器描述如下:

​ 其中,UID1、UID2均为16位,UID3为32位,UID4为32位。

​ 这样就可以从0x0x1FFFF7E8地址读取3个32bit的数据,获取它的唯一ID。

​ 【2】程序加密逻辑说明

​ 在讲解简单的程序加密方法之前,先了解一下通常的MCU程序复制方法。

​ 通过烧写器(或者其他手段),将MCU内部程序存储区中的内容全部读出,就获取了烧写文件。如果烧写文件没有被加密,则可以直接将它烧写到别的MCU中运行,就完成了复制。

​ 有没有办法可以使得对方即使获取了烧写文件,也不能直接烧写到别的MCU中运行呢?

​ 我们可以利用芯片的唯一ID来实现一种简单的程序加密方法。由于每个芯片的唯一ID是不同的,我们只要在程序中检查ID的合法性即可,主要的步骤如下:

​ (i)程序首次运行时,读出芯片的唯一ID;

​ (ii)将ID使用加密算法,计算出一个特殊值,写入到非易失存储区中(需要掉电能保存);

​ (iii)以后每次运行时,读出芯片唯一ID,通过算法计算后,与非易失存储区中保存的值比对,如果一致,则正常运行;如果不对,停止运行。

​ 由于每个芯片中的ID是不同的,由加密算法算出的值也是不同的,所以,即使有人获取了我们的烧写文件,烧写到另一片MCU中,由于ID不同,最后也会比对出错,无法执行。

三 设计实现

​ 【1】编写获取并加密ID的接口:

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
#define UID_BASE (0x1FFFF7E8)







/**************************************************************************



* 函数名称: getEncryptID



* 功能描述: 获取并加密ID



**************************************************************************/



u32 getEncryptID(u32 *p_id)



{



p_id[0] = *(vu32*)(UID_BASE);



p_id[1] = *(vu32*)(UID_BASE + 4);



p_id[2] = *(vu32*)(UID_BASE + 8);







return ((p_id[0] >> 3) + (p_id[1] >> 1) + (p_id[2] >> 2)); //加密算法



}

​ 【2】编写判断加密ID的接口:

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
97
98
99
100
101
102
103
104
105
const u32 uid_save = 0xFFFFFFFF;    //CPUID保存







/**************************************************************************



* 函数名称: judgeEncryptID



* 功能描述: 判断加密ID



**************************************************************************/



u8 judgeEncryptID(void)



{



u32 t_encid;



u32 t_cpuid[3];



u32 t_addr = 0;



u32 t_flashid;







t_encid = getEncryptID(t_cpuid); //读本机UID







t_addr = (u32)&uid_save;



if(*(vu32 *)t_addr == 0xFFFFFFFF) //第一次烧写将加密后的UID写入FLASH



{



FLASH_Unlock();



FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);



FLASH_ProgramWord(t_addr, t_encid);



FLASH_Lock();



}



t_flashid = *(vu32 *)t_addr; //第一次上电存完了之后需要再读一遍







return (t_flashid == t_encid);



}

​ 【3】主函数初始化中执行上面的判断:

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
void main(void)



{



//...



//判断加密UID(若不匹配,则报错并陷入死循环)



if(! judgeEncryptID())



{



while(1);



}







while(1)



{



//主循环



}



}

​ 【注意点】:

​ (i)注意 const u32 uid_save 的类型定义,我使用的 keil 环境中不能加 volatile 修饰,如果加了会被定义到RAM区,而非Flash区,这样起不到加密的效果。

​ 如果用的是IAR环境,可能必须加 volatile 修饰,同时工程选项Options->Debugger->Download中选择: use flash loader。不这样做的后果是按速度优化编译,再判断加密值,不会重新读取加密值,导致判断出错;

​ (ii)注意 const u32 uid_save 的初始化值定义为全F,和擦除后的状态一致,所以程序中省略了擦除的操作,可以直接写入;

​ (iii)唯一ID存储地址 0x1FFFF7E8 应该尽量隐蔽,尽量避免在程序中直接出现该值:

​ 更好的做法可以参考下面的方案:

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
/**************************************************************************



* 函数名称: getEncryptID



* 功能描述: 获取并加密ID



**************************************************************************/



u32 getEncryptID(u32 *p_id)



{



volatile u32 UID_BASE;



UID_BASE = 0x20000006; //让逆向的人误以为是ram变量



UID_BASE -= 0x800;



UID_BASE -= 0x1e; //等于id的基地址0x1FFFF7E8







p_id[0] = *(vu32*)(UID_BASE);



p_id[1] = *(vu32*)(UID_BASE + 4);



p_id[2] = *(vu32*)(UID_BASE + 8);







return ((p_id[0] >> 3) + (p_id[1] >> 1) + (p_id[2] >> 2)); //加密算法



}

参考资料

​ 【1】STM32 的加密实现_stn32加密-CSDN博客

​ 【2】【STM32+cubemx】0025 HAL库开发:唯一ID获取和简单的程序加密_mcu唯一uid加密-CSDN博客


相关链接(侵删)

  1. 【嵌入式】使用嵌入式芯片唯一ID进行程序加密实现

=================我是分割线=================

欢迎到公众号来唠嗑: