车规级MCU的SPI Flash安全编程:实现固件的签名校验与防篡改机制
外部SPI Flash存固件最忌明文裸奔:编程器一夹就能dump完整固件,中间人改包也不会被察觉。车规做法不是“加个CRC”,而是Bootloader启动前完成信任链校验——哈希保完整、签名保来源、版本计数器防回滚。
一、信任根别放普通Flash
SPI Flash本身不可信,公钥哈希/根公钥必须固化在芯片OTP、eFuse或HSM安全区,CPU主域读不到明文。
分层建议:
根公钥哈希 → OTP/eFuse(一次烧写不可逆)
App签名公钥 → HSM密钥槽,支持OTA轮换
私钥 → 厂内离线签名服务器,绝不下发到产线
BootROM或一级Bootloader先用固化公钥验二级BL,二级BL再验App,任一级失败即终止启动。
二、固件头结构设计
固件镜像头部带元数据,Bootloader按偏移解析:
typedef struct __attribute__((packed)) {
uint32_t magic; // 0x53454342 "SECB"
uint32_t version; // 单调版本号,防回滚
uint32_t img_len; // 不含header+signature
uint8_t hash[32]; // 固件体SHA-256
uint8_t sig[64]; // ECDSA/RSA签名
uint32_t svn; // 安全版本号
} fw_header_t;
固件体从header之后开始,签名对象通常是“header(不含sig)+固件体哈希”或整段固件摘要,具体按签名工具约定来。
三、Bootloader校验流程
SPI读按页搬进内部RAM缓冲,别逐字节调SPI函数算哈希:
int secure_verify(uint32_t slot_addr){
fw_header_t h;
spi_flash_read(slot_addr, &h, sizeof(h));
if(h.magic != FW_MAGIC) return -1;
/* 1. 防回滚:版本不低于当前 */
if(h.svn < otp_read_svn()) return -2;
/* 2. 算固件体哈希 */
sha256_ctx ctx; sha256_init(&ctx);
uint8_t buf[256]; uint32_t off = sizeof(h), left = h.img_len;
while(left){
uint32_t n = left>256?256:left;
spi_flash_read(slot_addr+off, buf, n);
sha256_update(&ctx, buf, n); off+=n; left-=n;
}
uint8_t digest[32]; sha256_final(&ctx, digest);
if(memcmp(digest, h.hash, 32)) return -3; // 完整性失败
/* 3. 验签:交HSM,私钥不出安全域 */
if(hsm_verify(h.sig, h.hash, pub_from_otp()) != HSM_OK)
return -4; // 来源伪造
otp_commit_svn(h.svn); // 验过才升版本计数
return 0;
}
CRC只防比特翻转,验签才防篡改;两者别互相替代。
四、跳转前清场
验签通过也别立刻跳,和OTA同理:
void jump_app(uint32_t app_addr){
if((*(uint32_t*)app_addr & 0x2FFE0000) != 0x20000000) return;
__disable_irq();
SysTick->CTRL = 0;
RCC->CIR = 0;
SCB->VTOR = app_addr;
__set_MSP(*(uint32_t*)app_addr);
((void(*)(void))(*(uint32_t*)(app_addr+4)))();
}
VTOR不重映射、SysTick没关,是新固件“跳过去HardFault”的高发原因。
五、防篡改补充手段
写保护:SPI Flash WP#脚接MCU GPIO或HSM,常态拉低锁状态寄存器;升级时才临时解
Boot区锁:Bootloader所在扇区写保护,App越权也擦不到
调试口熔断:量产锁JTAG/SWD,或走HSM挑战响应开放
运行时自检:关键函数段定期重算哈希,异常进安全态
外部Flash加密:代码大必须外挂时,存AES密文,Bootloader用HSM密钥解密进内部RAM/IFlash再跑
防回滚计数:优先放OTP/eFuse或HSM受保护区,普通Flash扇区只做辅助标记,别当唯一真相源
六、量产落坑
公钥哈希烧OTP前,先拿测试密钥跑通整链再换量产密钥
签名私钥泄露=所有在产ECU信任崩塌,轮换靠HSM存App公钥实现
SVN只增不减,回滚攻击就是刷旧漏洞版,必须卡死
Bootloader预留恢复槽,验签失败进Recovery而非变砖
SPI时钟、4线模式、地址映射(3字节/4字节)要和烧录工具一致,否则头都读歪
按“OTP存根公钥+SPI Flash头+哈希+签名+Bootloader验签后跳转+SVN防回滚+WP#锁Flash”落地,外部SPI Flash即使被物理读出也只是密文/脏数据,车规固件篡改、降级、克隆三条路都能堵住。





