第一部分 安全模块固件代码细节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/GPIO1.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 写入失败编程后回读全 01. 检查 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.md2.1.2 MCUboot 关键文件功能详解文件功能常修改的地方bootutil.c启动核心逻辑查找分区、验证签名、选择启动镜像bootutil_verify_sig函数签名验证算法、bootutil_choose_image分区选择策略loader.c镜像加载器从 Flash 复制镜像到 SRAM/DRAMboot_copy_image函数复制策略、boot_swap双分区切换crypto_common.c加密抽象层定义bootutil_sha256、bootutil_rsa_verify等添加新的加密算法如 SM2/SM3encryption_*.c固件加密实现AES-128/256 加密镜像boot_encrypt_image、boot_decrypt_image加密/解密逻辑imgtool.py镜像签名工具命令行签名、生成公钥添加新的签名格式或密钥类型flash_map.cFlash 分区映射定义分区布局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.md2.2.2 TFM 关键文件功能详解文件功能常修改的地方spm.h安全分区管理器定义分区配置、内存隔离spm_partition_config添加新分区、spm_memory_isolation内存隔离策略psa_crypto.hPSA 加密接口算法定义、密钥管理添加自定义加密算法如 SM2/SM3tfm_veneers.c安全呼叫 veneer非安全世界调用安全服务的入口添加新的安全服务函数tfm_veneer_new_servicetfm_crypto_api.c加密 API 实现AES、RSA、SHA 等替换底层加密引擎硬件加速/软件实现tfm_secure_storage.c安全存储 API读写、完整性保护修改存储后端Flash/OTP/SRAMplatform/*平台移植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.2TLS 1.3mTLS (双向认证)DTLS (UDP)协议版本RFC 5246RFC 8446基于 TLS 1.2/1.3RFC 6347握手轮数2 轮 (4 个消息)1 轮 (2 个消息)1 轮 (2 个消息)1 轮 (2 个消息)支持算法RSA、ECDHE、DHEECDHE 必须RSA 可选同 TLS同 TLS 1.3加密套件多种组合5 种标准套件同 TLS同 TLS 1.3证书验证可配置强制验证双向验证同 TLS性能2 RTT 握手1 RTT 握手 (0-RTT 可用)略高略高嵌入式支持mbedTLS, wolfSSLmbedTLS 3.0需额外配置mbedTLS 支持2.3.1 在嵌入式中的性能对比设备TLS 1.2 内存使用TLS 1.3 内存使用握手时间 (1KB 数据)Cortex-M3 (100MHz)8KB RAM6KB RAM800msCortex-M4 (200MHz)10KB RAM8KB RAM400msCortex-M7 (400MHz)12KB RAM10KB RAM200msRISC-V (500MHz)10KB RAM8KB RAM250ms2.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: - tags2.6.2 测试覆盖率要求测试类型覆盖率要求测试用例数量单元测试80% 行覆盖500集成测试90% 功能覆盖200安全测试100% 安全功能100故障注入90% 故障路径50渗透测试所有已知漏洞50硬件测试100% 寄存器访问1002.7 研发路线图季度主要任务交付物里程碑Q1- 实现 SM2/SM3/SM4 支持 - 集成 MCUboot 双分区- 算法实现 - 固件签名工具完成国密支持Q2- 硬件加速集成DMA AES - 固件压缩- 高效加密 API - lz4 压缩库加密性能提升 3 倍Q3- 远程认证 (DICE) - 侧信道防护- 设备身份验证 - 掩蔽实现通过 PSA Level 2Q4- 整体文档整理 - 开源发布- 技术白皮书 - 开发板支持通过 CE/CCC 认证2.8 总结安全芯片固件开发是一个综合性极强的领域涉及密码学、硬件设计、嵌入式软件、系统架构等多个学科。核心设计原则信任根不可篡改BOOTROM OTP 构建不可绕过的安全起点。最小化信任逐级验证每级仅信任下级一级。分层防御硬件隔离 加密 签名 认证。可恢复性双分区 回滚防止升级失败导致设备变砖。可观测性统计和日志便于调试和监控。推荐进阶路径先精通 MCUboot 和 TFM 的基础用法。学习硬件加密引擎的驱动开发。熟悉 OTP 和安全存储的设计。掌握安全调试和认证流程。参与 PSA 和 CCC/CE/FCC 认证项目。
芯片安全启动架构与信任之 TLS/SSL/mTLS 安全通信
发布时间:2026/6/4 21:34:49
第一部分 安全模块固件代码细节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/GPIO1.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 写入失败编程后回读全 01. 检查 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.md2.1.2 MCUboot 关键文件功能详解文件功能常修改的地方bootutil.c启动核心逻辑查找分区、验证签名、选择启动镜像bootutil_verify_sig函数签名验证算法、bootutil_choose_image分区选择策略loader.c镜像加载器从 Flash 复制镜像到 SRAM/DRAMboot_copy_image函数复制策略、boot_swap双分区切换crypto_common.c加密抽象层定义bootutil_sha256、bootutil_rsa_verify等添加新的加密算法如 SM2/SM3encryption_*.c固件加密实现AES-128/256 加密镜像boot_encrypt_image、boot_decrypt_image加密/解密逻辑imgtool.py镜像签名工具命令行签名、生成公钥添加新的签名格式或密钥类型flash_map.cFlash 分区映射定义分区布局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.md2.2.2 TFM 关键文件功能详解文件功能常修改的地方spm.h安全分区管理器定义分区配置、内存隔离spm_partition_config添加新分区、spm_memory_isolation内存隔离策略psa_crypto.hPSA 加密接口算法定义、密钥管理添加自定义加密算法如 SM2/SM3tfm_veneers.c安全呼叫 veneer非安全世界调用安全服务的入口添加新的安全服务函数tfm_veneer_new_servicetfm_crypto_api.c加密 API 实现AES、RSA、SHA 等替换底层加密引擎硬件加速/软件实现tfm_secure_storage.c安全存储 API读写、完整性保护修改存储后端Flash/OTP/SRAMplatform/*平台移植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.2TLS 1.3mTLS (双向认证)DTLS (UDP)协议版本RFC 5246RFC 8446基于 TLS 1.2/1.3RFC 6347握手轮数2 轮 (4 个消息)1 轮 (2 个消息)1 轮 (2 个消息)1 轮 (2 个消息)支持算法RSA、ECDHE、DHEECDHE 必须RSA 可选同 TLS同 TLS 1.3加密套件多种组合5 种标准套件同 TLS同 TLS 1.3证书验证可配置强制验证双向验证同 TLS性能2 RTT 握手1 RTT 握手 (0-RTT 可用)略高略高嵌入式支持mbedTLS, wolfSSLmbedTLS 3.0需额外配置mbedTLS 支持2.3.1 在嵌入式中的性能对比设备TLS 1.2 内存使用TLS 1.3 内存使用握手时间 (1KB 数据)Cortex-M3 (100MHz)8KB RAM6KB RAM800msCortex-M4 (200MHz)10KB RAM8KB RAM400msCortex-M7 (400MHz)12KB RAM10KB RAM200msRISC-V (500MHz)10KB RAM8KB RAM250ms2.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: - tags2.6.2 测试覆盖率要求测试类型覆盖率要求测试用例数量单元测试80% 行覆盖500集成测试90% 功能覆盖200安全测试100% 安全功能100故障注入90% 故障路径50渗透测试所有已知漏洞50硬件测试100% 寄存器访问1002.7 研发路线图季度主要任务交付物里程碑Q1- 实现 SM2/SM3/SM4 支持 - 集成 MCUboot 双分区- 算法实现 - 固件签名工具完成国密支持Q2- 硬件加速集成DMA AES - 固件压缩- 高效加密 API - lz4 压缩库加密性能提升 3 倍Q3- 远程认证 (DICE) - 侧信道防护- 设备身份验证 - 掩蔽实现通过 PSA Level 2Q4- 整体文档整理 - 开源发布- 技术白皮书 - 开发板支持通过 CE/CCC 认证2.8 总结安全芯片固件开发是一个综合性极强的领域涉及密码学、硬件设计、嵌入式软件、系统架构等多个学科。核心设计原则信任根不可篡改BOOTROM OTP 构建不可绕过的安全起点。最小化信任逐级验证每级仅信任下级一级。分层防御硬件隔离 加密 签名 认证。可恢复性双分区 回滚防止升级失败导致设备变砖。可观测性统计和日志便于调试和监控。推荐进阶路径先精通 MCUboot 和 TFM 的基础用法。学习硬件加密引擎的驱动开发。熟悉 OTP 和安全存储的设计。掌握安全调试和认证流程。参与 PSA 和 CCC/CE/FCC 认证项目。