1)STM32芯片的唯一ID简介
STM32内部有一个96bit的产品唯一ID,在出厂被固化在芯片中,是不能修改的,而且每个芯片不同,可以作为芯片的身份标识。
STM32不同系列的ID起始地址不同,如下所示:
0x1FFFF7AC, /*STM32F0唯一ID起始地址*/
0x1FFFF7E8, /*STM32F1唯一ID起始地址*/
0x1FFF7A10, /*STM32F2唯一ID起始地址*/
0x1FFFF7AC, /*STM32F3唯一ID起始地址*/
0x1FFF7A10, /*STM32F4唯一ID起始地址*/
0x1FF0F420, /*STM32F7唯一ID起始地址*/
0x1FF80050, /*STM32L0唯一ID起始地址*/
0x1FF80050, /*STM32L1唯一ID起始地址*/
0x1FFF7590, /*STM32L4唯一ID起始地址*/
0x1FF0F420; /*STM32H7唯一ID起始地址*/
所以,如果我们使用的是STM32F1系列的芯片,就可以从0x0x1FFFF7E8地址读取3个32bit的数据,获取它的唯一ID。
2)简单的程序加密方法
在讲解简单的程序加密方法之前,先了解一下通常的MCU程序复制方法。
通过烧写器(或者其他手段),将MCU内部程序存储区中的内容全部读出,就获取了烧写文件。如果烧写文件没有被加密,则可以直接将它烧写到别的MCU中运行,就完成了复制。
有没有办法可以使得,对方即使获取了烧写文件,也不能直接烧写到别的MCU中运行呢?
我们可以利用芯片的唯一ID来实现一种简单的程序加密方法。
由于每个芯片的唯一ID是不同的,我们只要在程序中检查ID的合法性即可,主要的步骤如下:
a)程序首次运行时,读出芯片的唯一ID;
b)将ID使用加密算法,计算出一个特殊的值,写入到非易失存储区中(需要掉电能保存);
c)以后每次运行时,读出芯片唯一ID,通过算法计算后,与非易失存储区中保存的值比对,如果一致,则正常运行;如果不对,停止运行。
由于每个芯片中的ID是不同的,由加密算法算出的值也是不同的,所以,即使有人获取了我们的烧写文件,烧写到另一片MCU中,由于ID不同,最后也会比对出错,无法执行。
3)程序实现
如下图配置,cubemx中选择了Uart1(用于打印信息和接收数据):
生成keil的工程文件,在keil中添加代码。
首先是获取唯一ID的函数,直接从地址中取数:
然后是加密函数,这里我们使用了简单的异或来实现:
写入flash的函数:
先定义了一个变量数组UID在flash中,把数据写入到UID的区域,则保存到了非易失存储区中:
读取flash中存储值的函数:
在main函数中,初始化后、while(1)前,编写加密、解密、及验证的代码:
实现的功能是:先读出ID,再加密;
接着读取UID的值,如果是全f,则认为是首次运行,把加密后的值存入UID中;
然后再读取UID中的值,比对读出的值和加密值,相等则验证通过;
不相等则认为,本芯片ID加密后的值与存储在UID中的值不一致,验证不通过。
4)程序测试
我们先将程序编译、下载到开发板,然后上电运行。
New ID write OK! 这行有输出,说明首次运行时,加密后的值被写入到了指定的地址。
第二次上电时,可以看到串口打印信息如下:
可以看到,这次没有New ID write OK! 这行的输出,直接到了读取验证环节,最后验证通过了。
5)注意事项
a)本文讲解的加密算法只是一种非常简易的方法,提供一种思路,实际工程应用还需要更完善的方法;
b)注意定义UID的方式:const uint32_t,并且初始化值为全F;const指定了定义到程序存储区;初始化值指定为全f,和擦除后的状态一致,所以程序中省略了擦除的操作,可以直接写入;
c)定义UID时,实测在keil环境中是不能加volatile的,如果加了,变量不会定义到flash区,而会被定义到ram区,是不能起到存储加密信息的作用的。
好了,本节内容就到这里了。
欢迎关注我的微信公众号“小白白学电子”,可留言“资料”获取所有源码和参考资料: