第一部分 安全模块固件代码细节

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_sha256bootutil_rsa_verify 添加新的加密算法(如 SM2/SM3)
encryption_*.c 固件加密实现:AES-128/256 加密镜像 boot_encrypt_imageboot_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 总结

安全芯片固件开发是一个综合性极强的领域,涉及密码学、硬件设计、嵌入式软件、系统架构等多个学科。

核心设计原则:

  1. 信任根不可篡改:BOOTROM + OTP 构建不可绕过的安全起点。

  2. 最小化信任:逐级验证,每级仅信任下级一级。

  3. 分层防御:硬件隔离 + 加密 + 签名 + 认证。

  4. 可恢复性:双分区 + 回滚,防止升级失败导致设备变砖。

  5. 可观测性:统计和日志,便于调试和监控。

推荐进阶路径:

  1. 先精通 MCUboot 和 TFM 的基础用法。

  2. 学习硬件加密引擎的驱动开发。

  3. 熟悉 OTP 和安全存储的设计。

  4. 掌握安全调试和认证流程。

  5. 参与 PSA 和 CCC/CE/FCC 认证项目。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐