3. 使用SHA-256哈希验证固件签名
任务目标
在Bootloader中实现固件完整性验证:对接收到的固件计算SHA-256哈希,与固件签名中携带的哈希值进行比对,确保固件未被篡改且来源可信。本案例聚焦哈希计算与比对流程,签名解析部分使用固定测试向量模拟。
环境依赖
- STM32F4系列MCU(使用内置CRC单元辅助验证)
- STM32CubeIDE或Keil MDK-ARM
- STM32 HAL库(已包含在CubeIDE中)
- SHA-256实现:使用
mbedtls库(CubeMX中可通过Middleware添加)
关键完整代码
/* main.c - Bootloader固件哈希验证核心逻辑 */
#include "mbedtls/sha256.h"
#include "string.h"
/* 固件签名结构(简化:仅含哈希值,实际工程中为RSA/ECDSA签名) */
typedef struct {
uint8_t hash[32]; /* SHA-256哈希值 */
uint32_t version; /* 固件版本号 */
} firmware_signature_t;
/* 模拟从Flash固定地址读取签名 */
#define SIGNATURE_ADDR 0x08010000
#define FIRMWARE_ADDR 0x08020000
#define FIRMWARE_MAX_SIZE 0x10000 /* 64KB */
/* 计算固件哈希并与签名比对 */
uint8_t verify_firmware_hash(void) {
mbedtls_sha256_context ctx;
uint8_t calc_hash[32];
firmware_signature_t sig;
uint32_t fw_size;
/* 1. 读取签名(实际工程中需先验证签名本身的有效性) */
memcpy(&sig, (void*)SIGNATURE_ADDR, sizeof(sig));
/* 2. 获取固件实际大小(存储在固定位置) */
fw_size = *(__IO uint32_t*)0x0801FFFC;
if (fw_size == 0 || fw_size > FIRMWARE_MAX_SIZE) {
return 0; /* 大小非法 */
}
/* 3. 计算固件区域的SHA-256 */
mbedtls_sha256_init(&ctx);
mbedtls_sha256_starts(&ctx, 0); /* 0 = SHA-256,非SHA-224 */
/* 分块读取Flash,避免一次性加载大固件到RAM */
uint8_t buffer[512];
uint32_t offset = 0;
while (offset < fw_size) {
uint32_t chunk = (fw_size - offset) > sizeof(buffer) ?
sizeof(buffer) : (fw_size - offset);
memcpy(buffer, (void*)(FIRMWARE_ADDR + offset), chunk);
mbedtls_sha256_update(&ctx, buffer, chunk);
offset += chunk;
}
mbedtls_sha256_finish(&ctx, calc_hash);
mbedtls_sha256_free(&ctx);
/* 4. 常量时间比较,防止时序攻击 */
uint8_t diff = 0;
for (int i = 0; i < 32; i++) {
diff |= calc_hash[i] ^ sig.hash[i];
}
return (diff == 0) ? 1 : 0; /* 1=验证通过 */
}
/* 跳转到固件执行 */
void jump_to_firmware(void) {
if (verify_firmware_hash()) {
/* 设置MSP并跳转 */
uint32_t msp = *(volatile uint32_t*)FIRMWARE_ADDR;
void (*jump)(void) = (void (*)(void))(*(volatile uint32_t*)(FIRMWARE_ADDR + 4));
__set_MSP(msp);
jump();
} else {
/* 验证失败,进入错误处理 */
while(1); /* 实际工程中可点亮LED或进入DFU模式 */
}
}
运行与验证
- 生成测试固件:编译一个简单的LED闪烁程序,将其二进制文件放置到
FIRMWARE_ADDR对应的Flash区域。 - 计算参考哈希:在PC上使用
sha256sum命令计算固件二进制文件的哈希值:
sha256sum firmware.bin - 填充签名结构:将上述哈希值按小端序写入
SIGNATURE_ADDR地址(前32字节),版本号写入后续4字节。 - 验证流程:在
main()中调用jump_to_firmware(),观察程序是否跳转到固件执行(如LED闪烁)。
通过条件
- 当签名中的哈希与固件实际哈希一致时,程序跳转成功,外设正常运行。
- 修改固件任意一个字节后重新烧录,程序停留在错误处理循环(不跳转),证明哈希比对有效。
- 使用调试器在
verify_firmware_hash()返回处设置断点,观察返回值:正确固件返回1,篡改固件返回0。
常见问题
- Flash读取速度慢:使用DMA或增加分块缓冲区大小(如1024字节)可提升哈希计算速度。
- 签名本身被篡改:本案例仅验证固件哈希,实际Bootloader中需先用公钥验证签名的RSA/ECDSA部分,再提取哈希比对。
- 内存不足:mbedtls SHA-256上下文约需100字节RAM,若资源紧张可改用轻量级实现(如
tiny-sha256)。