芯片安全启动架构与信任之 TLS/SSL/mTLS 安全通信
第一部分 安全模块固件代码细节
1.1 多核安全信息交互架构
现代SoC通常包含多个处理器核心(如Cortex-M4 + Cortex-M0,或RISC-V双核),安全模块需要处理多核之间的安全信息交互。核心挑战包括:
-
安全上下文隔离:安全核心与非安全核心之间的数据隔离。
-
核间通信:通过共享内存或Mail-box进行消息传递。
-
权限管理:确保只有授权核心能访问安全资源。
1.1.1 多核安全交互架构
+-------------------+ +-------------------+ | 安全核心 (S) | | 非安全核心 (NS) | | (Cortex-M4) | | (Cortex-M0) | +-------------------+ +-------------------+ | | | Mail-box 消息通道 | |<------------------------>| | (安全消息队列) | +-------------------+ | 共享安全内存 | | (加密存储区域) | +-------------------+ | 中断控制器 | | (核间中断) | +-------------------+
1.2 Mail-box驱动开发
Mail-box是一种硬件通信机制,允许核心之间通过寄存器传递消息。它通常包含发送寄存器、接收寄存器和状态寄存器。
1.2.1 Mail-box寄存器定义
/** * @file mailbox.h * @brief Mail-box 寄存器定义 */ /* Mail-box 基址(假设) */ #define MAILBOX_BASE 0x30000000 #define MAILBOX_SEND_REG (*(volatile uint32_t *)(MAILBOX_BASE + 0x00)) #define MAILBOX_RECV_REG (*(volatile uint32_t *)(MAILBOX_BASE + 0x04)) #define MAILBOX_STATUS_REG (*(volatile uint32_t *)(MAILBOX_BASE + 0x08)) #define MAILBOX_CTRL_REG (*(volatile uint32_t *)(MAILBOX_BASE + 0x0C)) #define MAILBOX_INT_REG (*(volatile uint32_t *)(MAILBOX_BASE + 0x10)) #define MAILBOX_INT_MASK (*(volatile uint32_t *)(MAILBOX_BASE + 0x14)) /* 状态寄存器位 */ #define MAILBOX_STATUS_TX_FULL (1 << 0) /* 发送缓冲区满 */ #define MAILBOX_STATUS_RX_EMPTY (1 << 1) /* 接收缓冲区空 */ #define MAILBOX_STATUS_TX_PENDING (1 << 2) /* 发送等待 */ #define MAILBOX_STATUS_RX_PENDING (1 << 3) /* 接收等待 */ /* 控制寄存器位 */ #define MAILBOX_CTRL_ENABLE (1 << 0) /* 启用 Mail-box */ #define MAILBOX_CTRL_INT_ENABLE (1 << 1) /* 启用中断 */ #define MAILBOX_CTRL_RESET (1 << 2) /* 复位 Mail-box */
1.2.2 Mail-box驱动实现
/**
* @file mailbox.c
* @brief Mail-box 驱动实现
*/
#include <stdint.h>
#include <stdbool.h>
#include "mailbox.h"
/**
* @brief 初始化 Mail-box
*/
void mailbox_init(void) {
/* 1. 复位 Mail-box */
MAILBOX_CTRL_REG |= MAILBOX_CTRL_RESET;
while (MAILBOX_CTRL_REG & MAILBOX_CTRL_RESET) {
/* 等待复位完成 */
}
/* 2. 启用 Mail-box */
MAILBOX_CTRL_REG = MAILBOX_CTRL_ENABLE | MAILBOX_CTRL_INT_ENABLE;
/* 3. 清空中断状态 */
MAILBOX_INT_REG = 0xFFFFFFFF;
/* 4. 启用中断(由中断控制器配置) */
}
/**
* @brief 发送消息到另一个核心
* @param msg 消息指针(32位)
* @param timeout_ms 超时时间(毫秒)
* @return 0 成功,-1 超时,-2 错误
*/
int mailbox_send(uint32_t msg, int timeout_ms) {
uint32_t timeout = 0;
while (MAILBOX_STATUS_REG & MAILBOX_STATUS_TX_FULL) {
/* 等待发送缓冲区可用 */
if (timeout_ms > 0) {
timeout++;
if (timeout > timeout_ms * 1000) {
return -1; /* 超时 */
}
}
}
/* 写入消息到发送寄存器 */
MAILBOX_SEND_REG = msg;
/* 触发核间中断(如果启用) */
MAILBOX_CTRL_REG |= (1 << 4); /* 触发接收端中断 */
return 0;
}
/**
* @brief 接收消息(非阻塞)
* @param msg 接收的消息指针
* @return 0 有消息,-1 无消息
*/
int mailbox_receive(uint32_t *msg) {
if (MAILBOX_STATUS_REG & MAILBOX_STATUS_RX_EMPTY) {
return -1; /* 无消息 */
}
*msg = MAILBOX_RECV_REG;
/* 清空中断标志 */
MAILBOX_INT_REG = (1 << 0);
return 0;
}
/**
* @brief Mail-box 中断服务函数
*/
void mailbox_irq_handler(void) {
uint32_t int_status = MAILBOX_INT_REG;
if (int_status & (1 << 0)) {
/* 收到新消息 */
uint32_t msg;
if (mailbox_receive(&msg) == 0) {
/* 处理消息(由上层调用) */
mailbox_process_message(msg);
}
/* 清空中断 */
MAILBOX_INT_REG = (1 << 0);
}
}
/**
* @brief 处理接收到的消息(示例)
* @param msg 消息内容
*/
void mailbox_process_message(uint32_t msg) {
/* 消息格式: [8位命令][8位源ID][16位数据] */
uint8_t cmd = (msg >> 24) & 0xFF;
uint8_t src_id = (msg >> 16) & 0xFF;
uint16_t data = msg & 0xFFFF;
switch (cmd) {
case 0x01: /* 安全请求 */
mailbox_handle_secure_request(src_id, data);
break;
case 0x02: /* 密钥请求 */
mailbox_handle_key_request(src_id, data);
break;
case 0x03: /* 状态查询 */
mailbox_handle_status_query(src_id);
break;
default:
/* 未知命令 */
break;
}
}
1.3 多核安全信息交互实现
多核之间传递安全信息时,需要对消息进行加密和签名,防止中间人攻击。
1.3.1 安全消息结构
/**
* @file secure_ipc.h
* @brief 安全核间通信消息结构
*/
#define SECURE_IPC_MAGIC 0x5A5A5A5A
#define SECURE_IPC_VERSION 0x02
typedef struct {
uint32_t magic; /* 魔数 */
uint32_t version; /* 版本 */
uint32_t sequence; /* 序列号 */
uint32_t timestamp; /* 时间戳 */
uint8_t src_core; /* 源核心 ID */
uint8_t dst_core; /* 目标核心 ID */
uint8_t cmd; /* 命令 */
uint8_t reserved; /* 保留 */
uint32_t payload_len; /* 载荷长度 */
uint8_t payload[256]; /* 载荷数据 */
uint8_t signature[256]; /* RSA 签名 */
} secure_ipc_msg_t;
typedef struct {
uint8_t key_id; /* 密钥 ID */
uint8_t key_type; /* 密钥类型 (AES/RSA/ECC) */
uint8_t key_len; /* 密钥长度 */
uint8_t key_data[64]; /* 密钥数据 (加密后) */
} ipc_key_msg_t;
1.3.2 安全消息发送与接收
/**
* @file secure_ipc.c
* @brief 安全核间通信实现
*/
#include "secure_ipc.h"
#include "crypto_rsa.h"
#include "crypto_sha.h"
#include "trng.h"
static uint32_t g_ipc_sequence = 0;
/**
* @brief 发送安全消息到另一个核心
* @param dst_core 目标核心 ID
* @param cmd 命令
* @param payload 载荷数据
* @param len 载荷长度
* @return 0 成功,-1 失败
*/
int secure_ipc_send(uint8_t dst_core, uint8_t cmd,
const uint8_t *payload, uint32_t len) {
secure_ipc_msg_t msg;
uint8_t hash[32];
/* 1. 构造消息 */
memset(&msg, 0, sizeof(msg));
msg.magic = SECURE_IPC_MAGIC;
msg.version = SECURE_IPC_VERSION;
msg.sequence = ++g_ipc_sequence;
msg.timestamp = get_system_time();
msg.src_core = get_current_core_id();
msg.dst_core = dst_core;
msg.cmd = cmd;
msg.payload_len = len;
if (len > 256) {
return -1;
}
memcpy(msg.payload, payload, len);
/* 2. 计算消息哈希 (不包括签名) */
crypto_sha256_hw((uint8_t *)&msg, sizeof(msg) - 256, hash);
/* 3. 用发送核心的私钥签名 */
uint8_t priv_key[256];
secure_storage_read(PRIV_KEY_ID, priv_key, &len);
rsa_sign_hw(priv_key, 256, hash, 32, msg.signature);
/* 4. 通过 Mail-box 发送 */
uint32_t *msg_words = (uint32_t *)&msg;
for (int i = 0; i < sizeof(msg) / 4; i++) {
if (mailbox_send(msg_words[i], 1000) != 0) {
return -1;
}
}
return 0;
}
/**
* @brief 接收并验证安全消息
* @param msg 接收的消息结构
* @return 0 成功,-1 验证失败,-2 序列号错误
*/
int secure_ipc_receive(secure_ipc_msg_t *msg) {
uint32_t *msg_words = (uint32_t *)msg;
/* 1. 从 Mail-box 接收 */
for (int i = 0; i < sizeof(secure_ipc_msg_t) / 4; i++) {
if (mailbox_receive(&msg_words[i]) != 0) {
return -1;
}
}
/* 2. 验证魔数和版本 */
if (msg->magic != SECURE_IPC_MAGIC ||
msg->version != SECURE_IPC_VERSION) {
return -1;
}
/* 3. 验证序列号 (防重放) */
static uint32_t last_seq[4] = {0}; /* 每个核心的序列号 */
if (msg->sequence <= last_seq[msg->src_core]) {
return -2; /* 重放攻击 */
}
last_seq[msg->src_core] = msg->sequence;
/* 4. 验证签名 */
uint8_t hash[32];
crypto_sha256_hw((uint8_t *)msg, sizeof(secure_ipc_msg_t) - 256, hash);
uint8_t pub_key[256];
secure_storage_read(PUB_KEY_ID + msg->src_core, pub_key, &len);
int ret = rsa_verify_hw(pub_key, 256, hash, 32, msg->signature);
if (ret != 1) {
return -1;
}
return 0;
}
1.4 安全引擎 BOOTROM 开发
BOOTROM 是芯片复位后执行的第一段代码,它必须足够小、不可修改,并且能够验证后续固件。
1.4.1 BOOTROM 启动流程
[芯片上电复位] -> [BOOTROM 入口] | +-> 1. 初始化 CPU 基本状态 (栈指针、中断向量) | +-> 2. 检查 OTP 状态 (锁定标志、公钥哈希) | +-> 3. 从 Flash 读取第一级引导固件头部 | +-> 4. 验证固件签名 (RSA-2048) | +-> 验证通过: 跳转到固件入口 | +-> 验证失败: 进入安全故障状态 | +-> 5. 如果验证通过,跳转到固件入口 | +-> 固件执行,继续加载后续代码 | +-> 6. 安全故障状态: +-> 停止 CPU,发送错误指示(LED/GPIO)
1.4.2 BOOTROM 代码实现(汇编 + C)
/** * @file bootrom.s * @brief BOOTROM 汇编启动代码 */ /* RISC-V BOOTROM 启动代码 */ .section .bootrom, "ax" .globl _start _start: /* 1. 设置栈指针 (使用内部 SRAM) */ li sp, 0x20004000 /* 2. 初始化中断向量 */ la t0, _start csrw mtvec, t0 /* 3. 清空缓存 */ li t0, 0 csrw mstatus, t0 /* 4. 调用 C 主函数 */ call bootrom_main /* 5. 如果返回,进入安全故障 */ li a0, 0xDEADBEEF j secure_fault /* 安全故障处理 */ secure_fault: /* 亮红灯、停止 CPU */ li t0, 0x10000000 /* GPIO 基址 */ li t1, 0x80000000 sw t1, 0(t0) /* 进入无限循环 */ wfi j secure_fault
/**
* @file bootrom.c
* @brief BOOTROM C 代码实现
*/
#include <stdint.h>
#include <string.h>
/* BOOTROM 常量和结构 */
#define BOOTROM_MAGIC 0x5AFE5AFE
#define BOOTROM_HEADER_SIZE 64
#define BOOTROM_SIGNATURE_SIZE 256
/* OTP 基址 */
#define OTP_BASE 0x10000000
#define OTP_ROOT_HASH (OTP_BASE + 0x100)
/* Flash 基址 */
#define FLASH_BASE 0x20000000
#define FW_HEADER_ADDR FLASH_BASE
/* 固件头部结构 */
typedef struct __attribute__((packed)) {
uint32_t magic;
uint32_t header_size;
uint32_t payload_size;
uint32_t version;
uint32_t boot_address;
uint8_t reserved[16];
uint8_t fw_hash[32];
uint8_t signature[256];
} fw_header_t;
/* 硬件抽象函数 (由 BOOTROM 实现) */
int otp_read(uint32_t addr, uint8_t *buf, uint32_t len) {
/* OTP 硬件读取 */
for (uint32_t i = 0; i < len; i++) {
buf[i] = *((uint8_t *)(OTP_BASE + addr + i));
}
return 0;
}
int flash_read(uint32_t addr, uint8_t *buf, uint32_t len) {
/* Flash 硬件读取 */
for (uint32_t i = 0; i < len; i++) {
buf[i] = *((uint8_t *)(FLASH_BASE + addr + i));
}
return 0;
}
void sha256_hw(const uint8_t *data, uint32_t len, uint8_t *digest) {
/* 调用硬件 SHA-256 引擎 */
/* 实际代码需要配置 SHA 引擎寄存器 */
/* 这里使用软件模拟 */
extern void sha256_sw(const uint8_t *, uint32_t, uint8_t *);
sha256_sw(data, len, digest);
}
int rsa_verify_hw(const uint8_t *pubkey, const uint8_t *hash,
const uint8_t *signature) {
/* 调用硬件 RSA 验签引擎 */
/* 实际代码需要配置 RSA 引擎寄存器 */
/* 返回 1 成功,0 失败 */
return 1; /* 模拟成功 */
}
/**
* @brief BOOTROM 主函数 (从汇编跳转进入)
*/
void bootrom_main(void) {
fw_header_t header;
uint8_t otp_root_hash[32];
uint8_t fw_hash[32];
uint8_t pubkey[256];
/* 1. 读取 OTP 根公钥哈希 */
if (otp_read(OTP_ROOT_HASH, otp_root_hash, 32) != 0) {
goto secure_fault;
}
/* 2. 读取固件头部 */
if (flash_read(0, (uint8_t *)&header, sizeof(header)) != 0) {
goto secure_fault;
}
/* 3. 验证魔数 */
if (header.magic != BOOTROM_MAGIC) {
goto secure_fault;
}
/* 4. 读取公钥 (从 OTP 或 Flash) */
if (otp_read(OTP_BASE + 0x200, pubkey, 256) != 0) {
goto secure_fault;
}
/* 5. 计算固件哈希 */
uint8_t *fw_data = (uint8_t *)(FLASH_BASE + sizeof(header));
sha256_hw(fw_data, header.payload_size, fw_hash);
/* 6. 验证固件哈希 */
if (memcmp(fw_hash, header.fw_hash, 32) != 0) {
goto secure_fault;
}
/* 7. RSA 验签 */
if (rsa_verify_hw(pubkey, fw_hash, header.signature) != 1) {
goto secure_fault;
}
/* 8. 验证通过,跳转到固件入口 */
void (*entry)(void) = (void (*)(void))(FLASH_BASE + header.boot_address);
entry();
/* 不会执行到这里 */
return;
secure_fault:
/* 进入安全故障状态 */
while (1) {
__asm__ volatile ("wfi");
}
}
1.5 OTP 驱动开发与密钥存储
1.5.1 OTP 驱动实现(包括写保护)
/**
* @file otp_driver.c
* @brief OTP 驱动实现 (带写保护)
*/
#include <stdint.h>
#include <stdbool.h>
/* OTP 控制器寄存器 (假设基址 0x20000000) */
#define OTP_CTRL (*(volatile uint32_t *)(0x20000000))
#define OTP_STATUS (*(volatile uint32_t *)(0x20000004))
#define OTP_ADDR (*(volatile uint32_t *)(0x20000008))
#define OTP_WDATA (*(volatile uint32_t *)(0x2000000C))
#define OTP_RDATA (*(volatile uint32_t *)(0x20000010))
#define OTP_LOCK (*(volatile uint32_t *)(0x20000014))
/* OTP 控制位 */
#define OTP_CTRL_READ (1 << 0)
#define OTP_CTRL_WRITE (1 << 1)
#define OTP_CTRL_ERASE (1 << 2)
#define OTP_CTRL_LOCK (1 << 3)
#define OTP_CTRL_BUSY (1 << 4)
/* OTP 状态位 */
#define OTP_STATUS_READY (1 << 0)
#define OTP_STATUS_ERROR (1 << 1)
/* OTP 锁定区域 (每 4 字节一个锁定位) */
#define OTP_LOCK_OFFSET(addr) ((addr) / 4)
/**
* @brief 等待 OTP 控制器就绪
*/
static void otp_wait_ready(void) {
while (OTP_CTRL & OTP_CTRL_BUSY) {
/* 等待完成 */
}
}
/**
* @brief 检查 OTP 地址是否已锁定
* @param addr OTP 地址 (字节偏移)
* @return 1 已锁定,0 未锁定
*/
int otp_is_locked(uint32_t addr) {
uint32_t lock_word = OTP_LOCK >> (OTP_LOCK_OFFSET(addr) & 31);
return (lock_word & 1) ? 1 : 0;
}
/**
* @brief 从 OTP 读取数据
* @param addr OTP 地址 (字节偏移)
* @param buf 输出缓冲区
* @param len 读取长度 (字节)
* @return 0 成功,-1 失败
*/
int otp_read(uint32_t addr, uint8_t *buf, uint32_t len) {
otp_wait_ready();
for (uint32_t i = 0; i < len; i += 4) {
OTP_ADDR = addr + i;
OTP_CTRL = OTP_CTRL_READ;
otp_wait_ready();
uint32_t data = OTP_RDATA;
for (int j = 0; j < 4 && i + j < len; j++) {
buf[i + j] = (data >> (j * 8)) & 0xFF;
}
}
return 0;
}
/**
* @brief 写入 OTP (一次性操作)
* @param addr OTP 地址 (4 字节对齐)
* @param data 要写入的数据 (4 字节)
* @return 0 成功,-1 失败
*/
int otp_write(uint32_t addr, uint32_t data) {
if ((addr & 3) != 0) {
return -1; /* 地址必须 4 字节对齐 */
}
if (otp_is_locked(addr)) {
return -1; /* 已锁定 */
}
otp_wait_ready();
OTP_ADDR = addr;
OTP_WDATA = data;
OTP_CTRL = OTP_CTRL_WRITE;
otp_wait_ready();
if (OTP_STATUS & OTP_STATUS_ERROR) {
return -1;
}
return 0;
}
/**
* @brief 锁定 OTP 地址 (永久禁止写入)
* @param addr OTP 地址
* @return 0 成功,-1 失败
*/
int otp_lock_region(uint32_t addr) {
if ((addr & 3) != 0) {
return -1;
}
otp_wait_ready();
OTP_ADDR = addr;
OTP_CTRL = OTP_CTRL_LOCK;
otp_wait_ready();
if (OTP_STATUS & OTP_STATUS_ERROR) {
return -1;
}
return 0;
}
1.6 数字签名解密与验证流程
数字签名解密(即签名验签)是安全启动、安全升级、安全通信的核心。完整流程如下:
1.6.1 数字签名验证流程
[签名数据] -> [SHA-256 哈希计算] -> [RSA-2048 验签] -> [验证结果] | | | | | +-> [签名有效] -> 允许执行 | | +-> [签名无效] -> 拒绝执行 | | | +-> [使用公钥解密密文] | +-> [从 OTP 读取根公钥]
1.6.2 数字签名验证完整代码
/**
* @file sig_verify.c
* @brief 数字签名验证完整实现
*/
#include <stdint.h>
#include <string.h>
#include "crypto_sha.h"
#include "crypto_rsa.h"
#include "otp_driver.h"
#define SIGNATURE_SIZE 256
#define HASH_SIZE 32
#define RSA_KEY_SIZE 256
/**
* @brief 验证数字签名 (完整流程)
* @param data 原始数据
* @param data_len 数据长度
* @param signature 签名 (256 字节)
* @param key_id 公钥 ID (从 OTP 或安全存储读取)
* @return 1 有效,0 无效,-1 错误
*/
int verify_signature(const uint8_t *data, uint32_t data_len,
const uint8_t *signature, uint32_t key_id) {
uint8_t hash[HASH_SIZE];
uint8_t pubkey[RSA_KEY_SIZE];
int ret;
/* 1. 计算数据哈希 */
ret = crypto_sha256_hw(data, data_len, hash);
if (ret != 0) {
return -1;
}
/* 2. 从 OTP 或安全存储读取公钥 */
if (key_id < 16) {
/* 从 OTP 读取 */
ret = otp_read(OTP_BASE + 0x200 + key_id * 256, pubkey, RSA_KEY_SIZE);
} else {
/* 从安全存储读取 */
ret = secure_storage_read(key_id, pubkey, &len);
}
if (ret != 0) {
return -1;
}
/* 3. RSA 验签 */
ret = rsa_verify_hw(pubkey, RSA_KEY_SIZE, hash, HASH_SIZE, signature);
return ret; /* 1 有效,0 无效 */
}
/**
* @brief 加密数据并签名 (用于安全通信)
* @param plaintext 明文
* @param pt_len 明文长度
* @param ciphertext 输出密文
* @param signature 输出签名
* @param key_id 用于签名的私钥 ID
* @return 0 成功,-1 失败
*/
int sign_and_encrypt(const uint8_t *plaintext, uint32_t pt_len,
uint8_t *ciphertext, uint8_t *signature,
uint32_t key_id) {
uint8_t hash[HASH_SIZE];
uint8_t priv_key[RSA_KEY_SIZE];
uint8_t aes_key[32];
int ret;
/* 1. 生成随机 AES 密钥 */
trng_get_random(aes_key, 32);
/* 2. 使用 AES-GCM 加密数据 */
uint8_t iv[12];
trng_get_random(iv, 12);
uint8_t tag[16];
ret = aes_gcm_encrypt_hw(aes_key, 32, iv, 12,
plaintext, pt_len,
ciphertext + 12 + 16, tag);
if (ret != 0) {
return -1;
}
/* 3. 计算加密数据的哈希 */
ret = crypto_sha256_hw(ciphertext + 12 + 16, pt_len, hash);
if (ret != 0) {
return -1;
}
/* 4. 使用私钥签名哈希 */
ret = secure_storage_read(key_id, priv_key, &len);
if (ret != 0) {
return -1;
}
ret = rsa_sign_hw(priv_key, RSA_KEY_SIZE, hash, HASH_SIZE, signature);
if (ret != 1) {
return -1;
}
/* 5. 构造输出:IV + Tag + 密文 + 签名 */
memcpy(ciphertext, iv, 12);
memcpy(ciphertext + 12, tag, 16);
memcpy(ciphertext + 12 + 16 + pt_len, signature, 256);
return 0;
}
1.7 安全模块固件开发中的常见问题与调试
| 问题 | 现象 | 调试方法 |
|---|---|---|
| BOOTROM 无法启动 | 芯片复位后无响应 | 1. 检查 BOOTROM 是否被错误擦除 2. 检查 CPU 复位向量是否正确 3. 使用 JTAG 读取 BOOTROM 前几条指令 |
| OTP 写入失败 | 编程后回读全 0 | 1. 检查 OTP 编程电压和时序 2. 确认地址和数据类型是否对齐 3. 检查 OTP 是否已锁定 |
| Mail-box 消息丢失 | 发送后接收端收不到消息 | 1. 检查 Mail-box 中断是否启用 2. 检查接收缓冲区是否溢出 3. 增加发送等待超时 |
| 签名验证失败 | 固件无法通过安全启动 | 1. 检查公钥是否匹配 OTP 存储 2. 确认签名算法类型 (RSA/ECDSA) 3. 验证哈希计算是否正确 |
| 多核通信异常 | 核间消息校验失败 | 1. 检查序列号是否重复 (防重放) 2. 验证消息签名是否正确 3. 检查核心 ID 映射是否正确 |
| 安全存储数据损坏 | 读取数据完整性校验失败 | 1. 检查 Flash 存储区域的 CRC 2. 确认加密密钥是否正确 3. 检查 HMAC 计算是否一致 |
第二部分 开源安全组件分析与总结
2.1 MCUboot 文件树与功能分析
MCUboot 是用于微控制器的安全启动加载程序,支持双分区、签名验证、回滚保护等。以下是其文件树结构及功能分析。
2.1.1 MCUboot 文件树
mcuboot/ ├── boot/ │ ├── bootutil/ # 启动工具库 │ │ ├── include/ # 头文件 │ │ │ ├── bootutil/bootutil.h # 启动工具主接口 │ │ │ ├── bootutil/encrypted.h # 固件加密支持 │ │ │ └── bootutil/crc.h # CRC 校验函数 │ │ ├── src/ # 源码 │ │ │ ├── bootutil.c # 启动逻辑 (查找分区、验证镜像) │ │ │ ├── crypto_common.c # 加密抽象层 │ │ │ ├── encryption_*.c # 加密具体实现 │ │ │ └── loader.c # 镜像加载和引导 │ │ └── README │ ├── zephyr/ # Zephyr OS 移植 │ │ ├── prj.conf # 配置 │ │ ├── CMakeLists.txt │ │ └── src/ │ │ └── main.c # Zephyr 入口 │ ├── mynewt/ # Mynewt OS 移植 │ └── mbed/ # Mbed OS 移植 ├── docs/ # 文档 │ ├── design.md # 设计文档 │ ├── encrypted_images.md # 固件加密文档 │ ├── porting.md # 移植指南 │ └── signed_images.md # 签名镜像指南 ├── samples/ # 示例 │ └── blinky/ # 示例应用 ├── scripts/ # 工具脚本 │ ├── imgtool.py # 镜像签名工具 (Python) │ ├── imgtool_rsa.py # RSA 签名 │ ├── imgtool_ecdsa.py # ECDSA 签名 │ ├── keygen.py # 密钥生成工具 │ └── flash_map.py # Flash 分区映射 ├── ext/ # 外部依赖 │ └── mbedtls/ # Mbed TLS (用于加密) ├── include/ # 公共头文件 │ ├── bootutil_flash.h # Flash 操作抽象 │ └── bootutil_public.h # 公共 API ├── CMakeLists.txt ├── Kconfig └── README.md
2.1.2 MCUboot 关键文件功能详解
| 文件 | 功能 | 常修改的地方 |
|---|---|---|
| bootutil.c | 启动核心逻辑:查找分区、验证签名、选择启动镜像 | bootutil_verify_sig 函数(签名验证算法)、bootutil_choose_image(分区选择策略) |
| loader.c | 镜像加载器:从 Flash 复制镜像到 SRAM/DRAM | boot_copy_image 函数(复制策略)、boot_swap(双分区切换) |
| crypto_common.c | 加密抽象层:定义 bootutil_sha256、bootutil_rsa_verify 等 |
添加新的加密算法(如 SM2/SM3) |
| encryption_*.c | 固件加密实现:AES-128/256 加密镜像 | boot_encrypt_image、boot_decrypt_image(加密/解密逻辑) |
| imgtool.py | 镜像签名工具:命令行签名、生成公钥 | 添加新的签名格式或密钥类型 |
| flash_map.c | Flash 分区映射:定义分区布局 | flash_map 结构(根据芯片 Flash 布局修改) |
| Kconfig | 配置选项:启用加密、调试、回滚等 | MCUBOOT_ENCRYPT, MCUBOOT_SIGN_EC256 等 |
2.1.3 MCUboot 集成示例(Zephyr)
/**
* @file mcuboot_integrate.c
* @brief MCUboot 集成到 Zephyr 示例
*/
/* 1. 初始化 MCUboot */
int mcuboot_init(void) {
int ret = bootutil_init();
if (ret != 0) {
return -1;
}
/* 2. 选择启动镜像 */
int boot_result = bootutil_choose_image();
if (boot_result != 0) {
/* 所有镜像无效,进入恢复模式 */
return -2;
}
/* 3. 加载镜像 */
ret = bootutil_load_image();
if (ret != 0) {
return -3;
}
return 0;
}
/**
* @brief 验证镜像签名 (自定义实现)
* @param img_addr 镜像地址
* @param size 镜像大小
* @param pubkey 公钥 (从 OTP 读取)
* @return 0 成功,-1 失败
*/
int mcuboot_verify_custom(uint32_t img_addr, uint32_t size,
const uint8_t *pubkey) {
/* 计算哈希 */
uint8_t hash[32];
crypto_sha256_hw((uint8_t *)img_addr, size, hash);
/* 读取签名 (镜像末尾 256 字节) */
uint8_t signature[256];
flash_read(img_addr + size - 256, signature, 256);
/* RSA 验签 */
return rsa_verify_hw(pubkey, 256, hash, 32, signature);
}
2.2 TFM (Trusted Firmware-M) 文件树与功能分析
TFM 是 Arm 为 Cortex-M 处理器提供的安全固件框架,实现了 PSA 安全标准。
2.2.1 TFM 文件树
tfm/ ├── docs/ # 文档 │ ├── security/ # 安全设计文档 │ ├── user_guides/ # 用户指南 │ └── platform/ # 平台文档 ├── platform/ # 平台移植 │ ├── arm/ # Arm 平台 (Cortex-M) │ ├── nxp/ # NXP 平台 │ ├── stm32/ # STM32 平台 │ └── boards/ # 开发板配置 ├── secure_fw/ # 安全固件 │ ├── core/ # 核心服务 │ │ ├── spm.h # 安全分区管理器 │ │ ├── psa_crypto.h # PSA 加密接口 │ │ └── psa_attestation.h # PSA 认证服务 │ ├── partitions/ # 安全分区 │ │ ├── crypto/ # 加密服务 │ │ ├── attestation/ # 认证服务 │ │ ├── internal_trusted_storage/ # 内部安全存储 │ │ └── protected_storage/ # 受保护存储 │ └── services/ # 安全服务 │ ├── tfm_crypto_api.c # 加密 API │ ├── tfm_attestation_api.c # 认证 API │ └── tfm_secure_storage.c # 安全存储 API ├── interface/ # 安全/非安全接口 │ ├── include/ # 头文件 │ │ ├── psa/ # PSA 标准头文件 │ │ └── tfm_veneers.h # 安全呼叫 veneer │ └── src/ # 接口实现 │ └── tfm_veneers.c # veneer 函数 ├── non_secure/ # 非安全应用 │ ├── CMakeLists.txt │ └── src/ │ └── main_ns.c # 非安全主程序 ├── build/ # 构建目录 ├── CMakeLists.txt ├── Kconfig └── README.md
2.2.2 TFM 关键文件功能详解
| 文件 | 功能 | 常修改的地方 |
|---|---|---|
| spm.h | 安全分区管理器:定义分区配置、内存隔离 | spm_partition_config(添加新分区)、spm_memory_isolation(内存隔离策略) |
| psa_crypto.h | PSA 加密接口:算法定义、密钥管理 | 添加自定义加密算法(如 SM2/SM3) |
| tfm_veneers.c | 安全呼叫 veneer:非安全世界调用安全服务的入口 | 添加新的安全服务函数(tfm_veneer_new_service) |
| tfm_crypto_api.c | 加密 API 实现:AES、RSA、SHA 等 | 替换底层加密引擎(硬件加速/软件实现) |
| tfm_secure_storage.c | 安全存储 API:读写、完整性保护 | 修改存储后端(Flash/OTP/SRAM) |
| platform/* | 平台移植:Flash、串口、中断配置 | 根据芯片硬件修改 Flash 驱动、OTP 驱动 |
2.2.3 TFM 集成示例
/**
* @file tfm_integrate.c
* @brief TFM 集成示例
*/
#include "psa/crypto.h"
#include "tfm_veneers.h"
/**
* @brief TFM 初始化 (安全启动后调用)
*/
void tfm_init(void) {
psa_status_t status;
/* 1. 初始化 PSA 加密 */
status = psa_crypto_init();
if (status != PSA_SUCCESS) {
tfm_fault_handler(status);
}
/* 2. 初始化安全存储 */
status = psa_its_init();
if (status != PSA_SUCCESS) {
tfm_fault_handler(status);
}
/* 3. 初始化认证服务 */
status = psa_attestation_init();
if (status != PSA_SUCCESS) {
tfm_fault_handler(status);
}
}
/**
* @brief 使用 TFM 安全存储写入数据
* @param uid 存储 ID
* @param data 数据指针
* @param len 数据长度
* @return 0 成功,-1 失败
*/
int tfm_secure_storage_write(uint32_t uid, const uint8_t *data, uint32_t len) {
psa_status_t status;
/* 1. 检查 UID 合法性 */
if (uid >= 0x1000) {
return -1;
}
/* 2. 调用 TFM 安全存储 API (通过 veneer) */
status = tfm_psa_its_set(uid, len, data, 0);
if (status != PSA_SUCCESS) {
return -1;
}
return 0;
}
/**
* @brief 使用 TFM 加密服务 (AES-GCM)
* @param key 密钥
* @param key_len 密钥长度
* @param plain 明文
* @param pt_len 明文长度
* @param cipher 输出密文
* @param tag 输出认证标签
* @return 0 成功,-1 失败
*/
int tfm_aes_gcm_encrypt(const uint8_t *key, size_t key_len,
const uint8_t *plain, size_t pt_len,
uint8_t *cipher, uint8_t *tag) {
psa_status_t status;
psa_key_handle_t key_handle;
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
size_t out_len;
/* 1. 设置密钥属性 */
psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
psa_set_key_algorithm(&attributes, PSA_ALG_GCM);
psa_set_key_bits(&attributes, key_len * 8);
/* 2. 导入密钥 */
status = psa_import_key(&attributes, key, key_len, &key_handle);
if (status != PSA_SUCCESS) {
return -1;
}
/* 3. AES-GCM 加密 */
uint8_t iv[12] = {0}; /* 实际应使用随机 IV */
status = psa_aead_encrypt(key_handle, PSA_ALG_GCM, iv, 12,
NULL, 0, plain, pt_len,
cipher, pt_len + 16, &out_len);
if (status != PSA_SUCCESS) {
psa_destroy_key(key_handle);
return -1;
}
/* 4. 提取 Tag */
memcpy(tag, cipher + pt_len, 16);
psa_destroy_key(key_handle);
return 0;
}
2.3 TLS/SSL/mTLS 安全通信方案树形对比
| 特性 | TLS 1.2 | TLS 1.3 | mTLS (双向认证) | DTLS (UDP) |
|---|---|---|---|---|
| 协议版本 | RFC 5246 | RFC 8446 | 基于 TLS 1.2/1.3 | RFC 6347 |
| 握手轮数 | 2 轮 (4 个消息) | 1 轮 (2 个消息) | 1 轮 (2 个消息) | 1 轮 (2 个消息) |
| 支持算法 | RSA、ECDHE、DHE | ECDHE 必须,RSA 可选 | 同 TLS | 同 TLS 1.3 |
| 加密套件 | 多种组合 | 5 种标准套件 | 同 TLS | 同 TLS 1.3 |
| 证书验证 | 可配置 | 强制验证 | 双向验证 | 同 TLS |
| 性能 | 2 RTT 握手 | 1 RTT 握手 (0-RTT 可用) | 略高 | 略高 |
| 嵌入式支持 | mbedTLS, wolfSSL | mbedTLS 3.0+ | 需额外配置 | mbedTLS 支持 |
2.3.1 在嵌入式中的性能对比
| 设备 | TLS 1.2 内存使用 | TLS 1.3 内存使用 | 握手时间 (1KB 数据) |
|---|---|---|---|
| Cortex-M3 (100MHz) | 8KB RAM | 6KB RAM | 800ms |
| Cortex-M4 (200MHz) | 10KB RAM | 8KB RAM | 400ms |
| Cortex-M7 (400MHz) | 12KB RAM | 10KB RAM | 200ms |
| RISC-V (500MHz) | 10KB RAM | 8KB RAM | 250ms |
2.4 整体架构评审(SWOT 分析)
| 维度 | 内容 |
|---|---|
| 优势 (Strengths) | - 完整的信任链:BOOTROM -> 固件 -> 应用 - 硬件安全模块:OTP、PMP、加密引擎 - 安全存储加密和完整性保护 - 多核安全通信 |
| 劣势 (Weaknesses) | - BOOTROM 固定且不可更新,一旦漏洞无法修补 - OTP 编程是一次性,无法更改配置 - 依赖外部工具签名(imgtool) - PMP 实现需要手动配置 |
| 机会 (Opportunities) | - 支持更多加密算法(SM2/SM3/SM4) - 集成 MCUboot 提高灵活性 - 使用 TEE/TrustZone 增强隔离 - 支持更高速接口(PCIe/USB3.0) |
| 威胁 (Threats) | - 侧信道攻击(功耗、电磁) - 故障注入攻击(电压、时钟) - 物理探针攻击(解密密钥) - 后门漏洞 |
2.5 未来演进路线
2.5.1 短期
| 项目 | 目标 | 技术方案 | 预计提升 |
|---|---|---|---|
| 支持更多加密算法 | 满足各国标准 | 集成 SM2/SM3/SM4 | 扩展认证范围 |
| 硬件加速集成 | 减少 CPU 占用 | 使用 FPGA/DMA 加速 | 加密性能 3-5 倍 |
| OTA 压缩 | 减少传输带宽 | 集成 lz4/zlib | 升级速度 2-3 倍 |
| 远程认证 | 设备身份验证 | 实现 RFC 8472 (DICE) | 增强安全性 |
2.5.2 中期
| 项目 | 目标 | 技术方案 | 预计提升 |
|---|---|---|---|
| 物理防攻击 | 抵抗侧信道攻击 | 使用掩蔽、随机化 | 提高安全性 |
| 区块链集成 | 设备身份溯源 | 在链上存储设备哈希 | 防止伪造 |
| 自愈机制 | 从攻击中恢复 | 自动备份和恢复 | 增加容错 |
| 量子安全 | 抗量子计算攻击 | 集成 PQC (如 FALCON, Dilithium) | 长期安全 |
2.6 持续集成与测试策略
2.6.1 CI/CD 流水线
# .gitlab-ci.yml stages: - build - test - security - deploy build-job: stage: build script: - make clean - make all - make bootrom - make fw_signed artifacts: paths: - build/bootrom.bin - build/fw_signed.bin test-job: stage: test script: - make test-unit - make test-integration - make test-fault-injection coverage: /Coverage: \d+\.\d+%/ security-job: stage: security script: - make security-scan - make sonarqube - make sbom variables: SONAR_TOKEN: $SONAR_TOKEN deploy-job: stage: deploy script: - make sign-firmware - make upload-firmware only: - tags
2.6.2 测试覆盖率要求
| 测试类型 | 覆盖率要求 | 测试用例数量 |
|---|---|---|
| 单元测试 | 80% 行覆盖 | 500+ |
| 集成测试 | 90% 功能覆盖 | 200+ |
| 安全测试 | 100% 安全功能 | 100+ |
| 故障注入 | 90% 故障路径 | 50+ |
| 渗透测试 | 所有已知漏洞 | 50+ |
| 硬件测试 | 100% 寄存器访问 | 100+ |
2.7 研发路线图
| 季度 | 主要任务 | 交付物 | 里程碑 |
|---|---|---|---|
| Q1 | - 实现 SM2/SM3/SM4 支持 - 集成 MCUboot 双分区 | - 算法实现 - 固件签名工具 | 完成国密支持 |
| Q2 | - 硬件加速集成(DMA + AES) - 固件压缩 | - 高效加密 API - lz4 压缩库 | 加密性能提升 3 倍 |
| Q3 | - 远程认证 (DICE) - 侧信道防护 | - 设备身份验证 - 掩蔽实现 | 通过 PSA Level 2 |
| Q4 | - 整体文档整理 - 开源发布 | - 技术白皮书 - 开发板支持 | 通过 CE/CCC 认证 |
2.8 总结
安全芯片固件开发是一个综合性极强的领域,涉及密码学、硬件设计、嵌入式软件、系统架构等多个学科。
核心设计原则:
-
信任根不可篡改:BOOTROM + OTP 构建不可绕过的安全起点。
-
最小化信任:逐级验证,每级仅信任下级一级。
-
分层防御:硬件隔离 + 加密 + 签名 + 认证。
-
可恢复性:双分区 + 回滚,防止升级失败导致设备变砖。
-
可观测性:统计和日志,便于调试和监控。
推荐进阶路径:
-
先精通 MCUboot 和 TFM 的基础用法。
-
学习硬件加密引擎的驱动开发。
-
熟悉 OTP 和安全存储的设计。
-
掌握安全调试和认证流程。
-
参与 PSA 和 CCC/CE/FCC 认证项目。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)