1. 初始化HSM模块并验证安全启动
任务说明
本案例面向集成硬件安全模块(HSM)的MCU平台,完成以下目标:初始化HSM外设、配置安全启动所需的密钥与固件校验策略、在启动流程中验证应用固件完整性,并在校验通过后跳转至应用代码执行。实现范围限定为:HSM驱动初始化、SHA-256摘要计算、基于预置密钥的固件签名验证,以及启动控制逻辑。
硬件连接与启动拓扑
代码实现
以下代码实现HSM初始化、固件哈希计算、签名验证与启动跳转的完整逻辑。代码基于C语言编写,适用于集成HSM核的MCU(如S32K3、RH850等),通过寄存器接口与HSM通信。
#include <stdint.h>
#include <string.h>
/* HSM寄存器映射(示例基地址) */
#define HSM_BASE_ADDR 0x40020000UL
#define HSM_CTRL_REG (*(volatile uint32_t *)(HSM_BASE_ADDR + 0x00))
#define HSM_STATUS_REG (*(volatile uint32_t *)(HSM_BASE_ADDR + 0x04))
#define HSM_DATA_IN_REG (*(volatile uint32_t *)(HSM_BASE_ADDR + 0x08))
#define HSM_DATA_OUT_REG (*(volatile uint32_t *)(HSM_BASE_ADDR + 0x0C))
#define HSM_CMD_REG (*(volatile uint32_t *)(HSM_BASE_ADDR + 0x10))
/* HSM命令定义 */
#define HSM_CMD_INIT 0xA1
#define HSM_CMD_LOAD_KEY 0xA2
#define HSM_CMD_HASH 0xA3
#define HSM_CMD_VERIFY 0xA4
/* 状态定义 */
#define HSM_STATUS_IDLE 0x00
#define HSM_STATUS_DONE 0x01
#define HSM_STATUS_ERROR 0xFF
/* 应用固件地址与大小(链接脚本定义) */
#define APP_START_ADDR 0x00020000UL
#define APP_MAX_SIZE 0x00010000UL /* 64KB */
/* 预置公钥(实际项目中由HSM烧录) */
static const uint8_t public_key[32] = {
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 0x01
};
/* 函数声明 */
static void hsm_wait_done(void);
static int hsm_init(void);
static int hsm_load_key(const uint8_t *key, uint32_t len);
static int hsm_compute_hash(uint32_t addr, uint32_t len, uint8_t *hash_out);
static int hsm_verify_signature(const uint8_t *hash, const uint8_t *sig);
static void jump_to_app(uint32_t app_addr);
/* 等待HSM操作完成 */
static void hsm_wait_done(void) {
while ((HSM_STATUS_REG & 0xFF) != HSM_STATUS_DONE) {
/* 等待硬件完成 */
}
}
/* 初始化HSM模块 */
static int hsm_init(void) {
HSM_CTRL_REG = 0x01; /* 使能HSM */
HSM_CMD_REG = HSM_CMD_INIT;
hsm_wait_done();
return (HSM_STATUS_REG == HSM_STATUS_DONE) ? 0 : -1;
}
/* 加载公钥到HSM */
static int hsm_load_key(const uint8_t *key, uint32_t len) {
uint32_t i;
if (len != 32) return -1;
for (i = 0; i < len; i += 4) {
uint32_t word = (uint32_t)key[i] |
((uint32_t)key[i+1] << 8) |
((uint32_t)key[i+2] << 16) |
((uint32_t)key[i+3] << 24);
HSM_DATA_IN_REG = word;
}
HSM_CMD_REG = HSM_CMD_LOAD_KEY;
hsm_wait_done();
return (HSM_STATUS_REG == HSM_STATUS_DONE) ? 0 : -1;
}
/* 计算固件哈希 */
static int hsm_compute_hash(uint32_t addr, uint32_t len, uint8_t *hash_out) {
uint32_t i;
/* 输入固件地址和长度 */
HSM_DATA_IN_REG = addr;
HSM_DATA_IN_REG = len;
HSM_CMD_REG = HSM_CMD_HASH;
hsm_wait_done();
if (HSM_STATUS_REG != HSM_STATUS_DONE) return -1;
/* 读取32字节哈希结果 */
for (i = 0; i < 32; i += 4) {
uint32_t word = HSM_DATA_OUT_REG;
hash_out[i] = (uint8_t)(word & 0xFF);
hash_out[i+1] = (uint8_t)((word >> 8) & 0xFF);
hash_out[i+2] = (uint8_t)((word >> 16) & 0xFF);
hash_out[i+3] = (uint8_t)((word >> 24) & 0xFF);
}
return 0;
}
/* 验证固件签名 */
static int hsm_verify_signature(const uint8_t *hash, const uint8_t *sig) {
uint32_t i;
/* 输入哈希值 */
for (i = 0; i < 32; i += 4) {
uint32_t word = (uint32_t)hash[i] |
((uint32_t)hash[i+1] << 8) |
((uint32_t)hash[i+2] << 16) |
((uint32_t)hash[i+3] << 24);
HSM_DATA_IN_REG = word;
}
/* 输入签名(假设64字节ECDSA签名) */
for (i = 0; i < 64; i += 4) {
uint32_t word = (uint32_t)sig[i] |
((uint32_t)sig[i+1] << 8) |
((uint32_t)sig[i+2] << 16) |
((uint32_t)sig[i+3] << 24);
HSM_DATA_IN_REG = word;
}
HSM_CMD_REG = HSM_CMD_VERIFY;
hsm_wait_done();
return (HSM_STATUS_REG == HSM_STATUS_DONE) ? 0 : -1;
}
/* 跳转到应用代码 */
static void jump_to_app(uint32_t app_addr) {
void (*app_entry)(void) = (void (*)(void))app_addr;
/* 设置主栈指针(应用向量表首字) */
__asm volatile ("LDR sp, [%0]" :: "r"(app_addr));
/* 跳转到复位向量 */
app_entry();
}
/* 安全启动主流程 */
int secure_boot_main(void) {
uint8_t hash[32];
/* 签名存储位置:固件头部偏移32字节处 */
const uint8_t *signature = (const uint8_t *)(APP_START_ADDR + 32);
/* 步骤1:初始化HSM */
if (hsm_init() != 0) {
return -1;
}
/* 步骤2:加载公钥 */
if (hsm_load_key(public_key, sizeof(public_key)) != 0) {
return -2;
}
/* 步骤3:计算应用固件哈希(跳过头部签名区) */
if (hsm_compute_hash(APP_START_ADDR + 64, APP_MAX_SIZE - 64, hash) != 0) {
return -3;
}
/* 步骤4:验证签名 */
if (hsm_verify_signature(hash, signature) != 0) {
/* 校验失败,停机 */
while (1) {
/* 可在此处点亮错误LED */
}
}
/* 步骤5:校验通过,跳转应用 */
jump_to_app(APP_START_ADDR + 4);
return 0; /* 不应到达此处 */
}
编译与链接配置
应用固件需在链接脚本中定义正确的内存布局:向量表位于APP_START_ADDR,签名数据存放于向量表之后偏移32字节处,实际代码从偏移64字节开始。HSM驱动依赖MCU的HSM硬件抽象层,需包含对应的寄存器定义头文件。
验证方法
功能验证:在集成开发环境中编译工程,通过调试器将程序烧录至MCU内部Flash。在secure_boot_main函数入口和跳转处设置断点,单步执行观察:
- HSM初始化后
HSM_STATUS_REG变为0x01(DONE状态); - 公钥加载完成后无错误返回;
- 哈希计算完成后
hash数组包含32字节非零数据; - 签名验证通过后程序跳转至应用入口地址。
判定标准:若篡改应用固件任意字节(如修改一个数据常量),重新烧录后程序应停留在签名验证失败的死循环中,不进入应用代码;恢复原始固件后启动流程恢复正常。通过以上行为对比,即可确认安全启动功能有效。