2、硬件信任根设计:基于OTP的密钥存储、安全OTP的物理防护机制、一次性编程与熔丝技术
各位同学,咱们今天聊点硬核的——硬件信任根。说白了,这就是整个安全体系的「地基」。你想想看,如果连这个根都不牢靠,上面盖再高的楼也是白搭。我在工业MCU项目里摸爬滚打这么多年,见过太多因为信任根设计不到位,导致整个固件被扒光的案例。嗯,今天咱们就把这块彻底讲透。
2.1 基于OTP的密钥存储:为什么非它不可?
OTP,全称是One-Time Programmable,一次性可编程。为什么安全启动非得用它?
我个人习惯把MCU的安全存储方案分成三类:
- RAM存储:掉电就丢,不适合存根密钥
- Flash/EEPROM:可反复擦写,但容易被攻击者通过电压毛刺、激光注入等方式篡改
- OTP:写进去就改不了,物理上锁死
我在项目中遇到过一件事:某客户用Flash存根密钥,结果被攻击者用FIB(聚焦离子束)切开了芯片封装,直接读出密钥。从那以后,我但凡涉及安全启动的项目,根密钥必须上OTP。
OTP存储的核心优势就三个字——不可逆。一旦编程,熔丝状态永久改变,想改回去?门儿都没有。
重要概念:OTP存储的密钥通常包括:
- 根公钥哈希(用于验证固件签名)
- 芯片唯一ID(用于绑定固件与硬件)
- 调试接口解锁密钥(防止调试端口被滥用)
2.2 安全OTP的物理防护机制:别让攻击者碰你的熔丝
光有OTP还不够。你得防着攻击者直接物理接触这些熔丝。我记得有一次做安全评估,发现某款MCU的OTP区域竟然没有顶层金属覆盖,攻击者用红外显微镜就能直接看到熔丝状态。这设计,说白了就是裸奔。
安全OTP的物理防护,我总结为「三层防御」:
| 防护层级 | 技术手段 | 作用 |
|---|---|---|
| 第一层 | 顶层金属网格(Active Shield) | 覆盖OTP区域,一旦被切割或钻孔,触发复位或擦除 |
| 第二层 | 传感器网络(光、温度、电压) | 检测到异常环境(如激光照射、低温、电压毛刺),立即熔断关键熔丝 |
| 第三层 | 冗余存储与校验 | 同一密钥存多份,读取时做多数表决,防止单点故障 |
你可能会问:「加这么多防护,成本扛得住吗?」嗯,这得看你的产品定位。工业级MCU,尤其是用在电表、汽车、工控领域的,这些防护一个都不能少。我见过一个电表方案,因为省了Active Shield,被攻击者用化学腐蚀法剥开芯片,直接读出了密钥——整个批次的产品都得召回。
警告:千万不要把OTP密钥区域放在芯片边缘。芯片边缘是封装切割的应力集中区,容易产生微裂纹,攻击者可以利用这些裂纹注入探针。我建议把OTP放在芯片核心区域,四周用冗余电路包围。
2.3 一次性编程与熔丝技术:从原理到实战
熔丝技术,说白了就是「烧断」或「烧通」一根微小的导线。目前主流的有两种:
- Poly熔丝(多晶硅熔丝):通过大电流烧断多晶硅,形成开路。优点是工艺成熟,缺点是编程时需要较大电流,容易影响周边电路。
- 反熔丝(Antifuse):原本是开路,编程后变成短路。优点是编程电流小,可靠性高。缺点是工艺稍复杂。
我个人更偏爱反熔丝方案。为什么?因为它的物理状态更稳定。我在一个项目中用过Poly熔丝,结果发现部分熔丝在高温老化测试后出现了「再生长」现象——明明烧断了,过段时间又恢复了部分导通。这要是用在安全启动上,后果不堪设想。
下面是一个典型的OTP编程流程伪代码,我习惯在固件中这样实现:
// OTP编程流程示例(伪代码)
void otp_program_key(uint32_t* key_data, uint32_t address) {
// 1. 检查OTP是否已被编程
if (otp_is_programmed(address)) {
// 一旦编程,永久锁定
error_handler("OTP already programmed!");
return;
}
// 2. 解锁OTP编程控制器
otp_unlock_controller();
// 3. 写入密钥数据(逐字写入)
for (int i = 0; i < KEY_WORDS; i++) {
otp_write_word(address + i, key_data[i]);
// 写后立即回读校验
if (otp_read_word(address + i) != key_data[i]) {
error_handler("OTP write verify failed!");
return;
}
}
// 4. 永久锁定该OTP区域(不可逆)
otp_lock_region(address, KEY_WORDS);
// 5. 锁定编程控制器,防止后续误操作
otp_lock_controller();
}
实战技巧:我曾经在调试一个项目时发现,OTP编程时如果电源纹波过大,会导致熔丝烧断不完全。我的建议是:编程期间开启内部LDO,关闭所有不必要的外设时钟,确保电源纯净。另外,编程完成后一定要做三次回读校验,别省这一步。
2.4 避坑指南:OTP设计中的那些「坑」
做OTP设计这么多年,我踩过的坑不少。挑几个典型的跟大家说说:
- 坑一:OTP容量预留不足。我曾经设计了一款MCU,只留了256字节OTP给密钥。结果客户要支持多级安全启动,需要存多个公钥哈希,根本不够用。后来改版时我建议至少预留1KB,给未来扩展留余地。
- 坑二:忽略了OTP的耐久性。虽然OTP只能写一次,但读操作没有限制。不过要注意,某些OTP单元在高温下读次数过多会退化。我建议在读取时加ECC校验,或者限制读取频率。
- 坑三:调试接口没锁死。这是最致命的。OTP里存了密钥,但调试接口(JTAG/SWD)还开着,攻击者直接通过调试接口读内存,密钥就暴露了。我的做法是:在OTP编程完成后,立即熔断调试接口的使能熔丝,物理上切断调试通路。
核心总结:硬件信任根的设计,本质上是一场「物理攻防战」。OTP提供了不可逆的存储,Active Shield提供了物理隔离,熔丝技术提供了可靠的编程手段。三者缺一不可。记住一句话:没有物理防护的OTP,等于把钥匙挂在门上。
好了,这一章的内容就到这里。下一章咱们聊聊「安全启动流程设计:从复位到固件校验的完整链路」,到时候我会结合一个实际项目的启动日志,带大家一步步分析。嗯,敬请期待。