2. 生成并存储设备唯一密钥
任务说明
本案例面向集成硬件安全模块(HSM)的MCU平台,目标为:调用HSM内置真随机数发生器(TRNG)生成128位设备唯一密钥,并将该密钥写入HSM内部非易失存储器(如OTP/eFlash安全区),确保密钥明文不经过MCU主处理器总线。完成本案例后,设备每次启动均可通过HSM会话句柄引用该密钥,用于后续加解密或签名运算。
HSM密钥生成与存储架构
HSM驱动抽象层实现
以下代码实现HSM密钥生成与存储的最小完整驱动。采用分层设计:底层为寄存器操作,上层为密钥管理API。密钥生成过程完全在HSM内部完成,MCU仅接收密钥句柄(32位整数),无法读取密钥明文。
/* hsm_driver.h */
#ifndef HSM_DRIVER_H
#define HSM_DRIVER_H
#include <stdint.h>
/* HSM寄存器映射(示例基地址) */
#define HSM_BASE_ADDR 0x40020000UL
#define HSM_TRNG_CTRL (HSM_BASE_ADDR + 0x00)
#define HSM_TRNG_DATA (HSM_BASE_ADDR + 0x04)
#define HSM_KEY_CTRL (HSM_BASE_ADDR + 0x10)
#define HSM_KEY_STORE_CMD (HSM_BASE_ADDR + 0x14)
#define HSM_KEY_HANDLE (HSM_BASE_ADDR + 0x18)
#define HSM_KEY_STATUS (HSM_BASE_ADDR + 0x1C)
/* 控制位定义 */
#define TRNG_ENABLE (1U << 0)
#define TRNG_DATA_READY (1U << 1)
#define KEY_GEN_START (1U << 0)
#define KEY_STORE_START (1U << 1)
#define KEY_OP_DONE (1U << 0)
#define KEY_OP_ERROR (1U << 1)
/* 密钥存储槽位(HSM内部非易失区) */
#define KEY_SLOT_DEVICE_UNIQUE 0x01U
/* API返回状态 */
#define HSM_OK 0
#define HSM_ERR_TRNG -1
#define HSM_ERR_STORE -2
#define HSM_ERR_PARAM -3
/* 密钥句柄类型 */
typedef uint32_t hsm_key_handle_t;
/* 寄存器读写宏(平台相关,此处为示例) */
#define REG32(addr) (*(volatile uint32_t *)(addr))
/* 驱动函数 */
int hsm_init(void);
int hsm_generate_device_key(hsm_key_handle_t *handle);
#endif /* HSM_DRIVER_H */
/* hsm_driver.c */
#include "hsm_driver.h"
/* 等待HSM操作完成 */
static int hsm_wait_done(uint32_t status_reg, uint32_t done_mask, uint32_t err_mask)
{
uint32_t timeout = 10000; /* 超时计数 */
uint32_t status;
while (timeout--) {
status = REG32(status_reg);
if (status & err_mask) {
return HSM_ERR_STORE; /* 操作错误 */
}
if (status & done_mask) {
return HSM_OK; /* 操作完成 */
}
}
return HSM_ERR_STORE; /* 超时 */
}
int hsm_init(void)
{
/* 使能TRNG模块 */
REG32(HSM_TRNG_CTRL) |= TRNG_ENABLE;
/* 等待TRNG就绪(自检完成) */
uint32_t timeout = 1000;
while (timeout--) {
if (REG32(HSM_TRNG_CTRL) & TRNG_DATA_READY) {
return HSM_OK;
}
}
return HSM_ERR_TRNG;
}
int hsm_generate_device_key(hsm_key_handle_t *handle)
{
if (handle == 0) {
return HSM_ERR_PARAM;
}
/* 步骤1: 触发HSM内部密钥生成(TRNG -> 密钥管理单元) */
REG32(HSM_KEY_CTRL) = KEY_GEN_START;
/* 等待密钥生成完成 */
int ret = hsm_wait_done(HSM_KEY_STATUS, KEY_OP_DONE, KEY_OP_ERROR);
if (ret != HSM_OK) {
return HSM_ERR_TRNG;
}
/* 步骤2: 将生成的密钥写入非易失存储槽位 */
REG32(HSM_KEY_STORE_CMD) = (KEY_STORE_START | (KEY_SLOT_DEVICE_UNIQUE << 8));
/* 等待存储完成 */
ret = hsm_wait_done(HSM_KEY_STATUS, KEY_OP_DONE, KEY_OP_ERROR);
if (ret != HSM_OK) {
return HSM_ERR_STORE;
}
/* 步骤3: 获取密钥句柄(用于后续密码运算引用) */
*handle = REG32(HSM_KEY_HANDLE);
return HSM_OK;
}
/* main.c 使用示例 */
#include <stdio.h>
#include "hsm_driver.h"
int main(void)
{
hsm_key_handle_t dev_key;
int ret;
/* 初始化HSM */
ret = hsm_init();
if (ret != HSM_OK) {
printf("HSM init failed: %d\n", ret);
return ret;
}
/* 生成并存储设备唯一密钥 */
ret = hsm_generate_device_key(&dev_key);
if (ret != HSM_OK) {
printf("Key generation failed: %d\n", ret);
return ret;
}
/* 打印密钥句柄(非密钥本身) */
printf("Device unique key handle: 0x%08X\n", dev_key);
printf("Key securely stored in HSM slot 0x%02X\n", KEY_SLOT_DEVICE_UNIQUE);
return 0;
}
编译与运行
本驱动面向集成HSM的MCU(如NXP S32K3、Infineon TC3xx或等效平台)。编译命令示例:
# 使用arm-none-eabi工具链
arm-none-eabi-gcc -mcpu=cortex-m7 -mthumb -Wall -O2 \
-I./include -c hsm_driver.c -o hsm_driver.o
arm-none-eabi-gcc -mcpu=cortex-m7 -mthumb -Wall -O2 \
-I./include -c main.c -o main.o
arm-none-eabi-ld -T platform.ld hsm_driver.o main.o -o hsm_test.elf
将生成的hsm_test.elf烧录至目标板,通过串口或调试器观察输出。
验证方法与判定标准
功能验证:
- 首次烧录运行,程序应打印
Device unique key handle: 0xXXXXXXXX,句柄为非零值。 - 复位重启后再次运行,若HSM非易失存储已写入,应返回相同的密钥句柄(表示密钥已持久化,未重新生成)。
- 若需验证密钥唯一性,可在不同设备上重复上述流程,句柄不同仅代表存储位置不同;真正的唯一性验证需通过HSM提供的密钥校验指令(如计算该密钥的CMAC并与安全存储的摘要比对),本案例驱动范围不包含该验证指令。
安全属性验证:
- 通过调试器读取HSM密钥存储区地址,应无法直接获得128位密钥明文(存储区受HSM硬件保护)。
- MCU内存中搜索密钥特征值,不应发现完整密钥材料。
判定标准:程序返回HSM_OK且句柄非零;复位后句柄不变;调试器无法读取密钥明文。满足以上三条即判定本案例通过。
注意:上述代码中的寄存器地址和位定义基于典型HSM架构抽象,实际平台需根据芯片参考手册调整。密钥句柄仅用于后续密码运算API引用,不代表密钥本身可被读取。