可信固件

1. 可信固件概述

可信固件是指通过加密、认证和完整性检验等多重安全措施,确保嵌入式设备只执行经过授权和验证的固件程序。可信固件系统的目标是防止未授权修改、恶意代码注入和固件回滚等安全威胁。

1.1 可信固件的核心安全属性

  • 真实性(Authenticity): 确保固件来源于可信发布者
  • 完整性(Integrity): 保证固件内容在传输和存储过程中未被篡改
  • 机密性(Confidentiality): 防止固件内容被未授权方获取和分析
  • 新鲜度(Freshness): 防止降级和回滚攻击,确保使用最新版本

1.2 传统固件更新与可信固件更新的区别

特性 传统固件更新 可信固件更新
传输方式 明文传输 加密传输
完整性检验 简单CRC/校验和 密码学哈希(SHA-256等)
身份验证 无或简单密码 数字签名
防回滚机制 通常无 版本控制与验证
防篡改措施 有限 全面加密与签名验证

2. 可信固件更新系统架构

2.1 系统组件

可信固件更新系统通常包含以下核心组件:

  1. 上位机工具: 负责固件打包、加密、签名和传输
  2. 通信中间层: 负责固件数据传输(如Ymodem协议)
  3. 应用程序(APP): 接收加密固件并存储到外部闪存
  4. Bootloader: 负责固件解密、验证和烧录

2.2 数据流程

[上位机] --> [加密固件] --> [APP程序] --> [外部Flash存储] --> [Bootloader解密验证] --> [内部Flash烧录]

2.3 存储布局

典型的系统存储布局示例:

内部Flash:
+----------------------+ 0x08000000
| Bootloader           | (包含解密和验证算法)
+----------------------+ 0x08008000 (32KB)
| 应用程序(APP)         |
+----------------------+ 0x0800FFFF

外部Flash:
+----------------------+ 0x00000000
| 固件更新标志区          |
+----------------------+ 0x00001000
| 固件版本存储区          |
+----------------------+ 0x00002000
| 加密固件存储区          |
+----------------------+ 0x????????

3. 可信固件结构设计

3.1 固件头部结构

为支持可信固件更新,设计了以下固件头部结构:

typedef struct {
    uint32_t magic;             // 魔术字 
    uint32_t version;           // 固件版本号 (格式: 主版本<<24 | 次版本<<16 | 修订号<<8 | 构建号)
    uint32_t size;              // 固件大小(不包含头部)
    uint8_t sha256[32];         // SHA-256哈希值 (原始固件的哈希,非加密固件)
    uint32_t header_crc;        // 头部CRC校验值
} FirmwareHeader_t;

3.2 完整固件包结构

完整的可信固件包结构如下:

+-------------------+
| 固件头部 (48字节)   |
+-------------------+
| 加密固件数据        | (AES-128-CBC加密)
+-------------------+

4. 安全机制详解

4.1 加密技术

  • 算法选择: AES-128-CBC (密码块链接模式)
  • 密钥管理:
    • 预共享密钥存储在Bootloader中
    • 密钥可通过安全通道更新或烧写
  • 初始化向量(IV): 固定IV或基于固件版本生成

4.2 完整性验证

  • SHA-256哈希: 在上位机计算原始固件的SHA-256值并存储在头部
  • CRC32校验: 用于验证固件头部和传输数据的完整性
  • 分层验证:
    1. 传输层使用CRC16验证包完整性
    2. 存储层使用CRC32验证数据完整性
    3. 应用层使用SHA-256验证固件真实性

4.3 版本控制与防回滚

  • 版本号格式: 主版本(8位) | 次版本(8位) | 修订号(8位) | 构建号(8位)
  • 版本号存储: 在外部Flash独立区域存储当前版本号
  • 版本比较逻辑: 只允许安装版本号大于当前版本的固件

5. 实现流程详解

5.1 上位机固件加密与打包流程

  1. 编译生成原始固件
  2. 计算固件SHA-256哈希值
  3. 创建并填充固件头部:
    • 设置魔术字(FIRM)
    • 设置版本号
    • 设置固件大小
    • 写入SHA-256哈希
    • 计算头部CRC32
  4. 使用AES-128-CBC加密固件内容
  5. 合并头部与加密固件数据
  6. 使用Ymodem协议传输完整固件包

5.2 APP程序接收与存储流程

  1. 初始化Ymodem接收器
  2. 接收固件数据包:
    • 验证每个数据包的CRC16
    • 临时存储到RAM缓冲区
  3. 写入外部Flash:
    • 擦除足够的存储空间
    • 写入固件数据
    • 验证写入是否成功
  4. 接收完成后:
    • 验证固件头部格式和CRC
    • 检查版本号,确保大于当前版本
    • 设置固件更新标志
  5. 重启系统,进入Bootloader

5.3 Bootloader验证与烧录流程

  1. 检测固件更新标志
  2. 从外部Flash读取固件头部
  3. 验证固件头部:
    • 检查魔术字
    • 验证头部CRC
    • 检查固件大小是否合理
  4. 检查版本号:
    • 读取当前版本号
    • 比较是否允许更新
  5. 解密固件数据:
    • 使用预设密钥解密
    • 计算解密后数据的SHA-256哈希
    • 与头部中存储的哈希比较
  6. 烧录内部Flash:
    • 擦除应用程序区域
    • 写入解密后的固件
    • 验证写入是否成功
  7. 更新版本信息
  8. 清除更新标志
  9. 重启进入新应用程序

6. 代码实现详解

以下是核心功能的代码实现方案,已根据您提供的设计流程进行优化:

6.1 IAP模块实现

IAP (In-Application Programming) 模块负责固件存储和验证:

/**
 * 验证固件头部
 * @param pHeader 固件头部指针
 * @return 验证状态
 */
FirmwareVerifyStatus_t IAP_Verify_Firmware_Header(const FirmwareHeader_t *pHeader)
{
    uint32_t calc_crc;
    
    // 检查魔术字,"FIRM" = 0x4649524D
    if (pHeader->magic != 0x4649524D) {
        return FIRMWARE_INVALID_MAGIC;
    }
    
    // 检查固件大小是否在有效范围内
    if (pHeader->size == 0 || pHeader->size > APP_FLASH_LEN) {
        return FIRMWARE_INVALID_SIZE;
    }
    
    // 计算头部CRC (不包括CRC字段本身)
    calc_crc = IAP_Calculate_CRC32((const uint8_t *)pHeader, sizeof(FirmwareHeader_t) - 4);
    if (calc_crc != pHeader->header_crc) {
        return FIRMWARE_INVALID_CRC;
    }
    
    return FIRMWARE_OK;
}

6.2 Ymodem接收模块优化

优化接收模块以支持加密固件头部处理:

/**
 * 完成验证跳转处理
 */
void Y_Modem_EndVerification_Jump()
{
    if(update_flag && ymodem_trans.trans_start == 0)
    {   
        if(ymodem_trans.File_Length > 0)
        {
            // 读取接收到的固件头部信息
            W25Q128_read((uint8_t*)&received_firmware_header, APP_SAVE_ADDR, sizeof(FirmwareHeader_t));
            
            // 验证固件头部
            FirmwareVerifyStatus_t header_status = IAP_Verify_Firmware_Header(&received_firmware_header);
            if (header_status != FIRMWARE_OK) {
                printf("\r\nFirmware header verification failed (code: %d)\r\n", header_status);
                // 擦除更新标志以防止无效固件被加载
                IAP_SPI_Flash_Erase_Flag();
                update_flag = 0;
                return;
            }
            
            // 验证版本号
            uint32_t current_version = IAP_Get_Version();
            if (current_version > 0 && received_firmware_header.version <= current_version) {
                printf("\r\nFirmware version too old, update rejected\r\n");
                // 擦除更新标志以防止回滚
                IAP_SPI_Flash_Erase_Flag();
                update_flag = 0;
                return;
            }
            
            // 传输成功,保存版本信息
            printf("\r\nFirmware update successful, size: %lu bytes\r\n", ymodem_trans.File_Length);
            
            // 保存新版本号
            IAP_Save_Version(received_firmware_header.version);
            
            // 写入更新标志
            IAP_SPI_Flash_Write_Flag();
            
            printf("System will restart shortly...\r\n");
            HAL_Delay(1000);  // 延时1秒
            Sys_Soft_Reset();  // 软件复位
        }
    }
}

6.3 Bootloader解密与验证实现

Bootloader中实现固件解密与验证:

/**
 * 解密并验证固件
 * @return 成功返回0,失败返回错误码
 */
int Bootloader_Decrypt_And_Verify_Firmware(void)
{
    FirmwareHeader_t firmware_header;
    uint8_t buffer[DECRYPT_BUFFER_SIZE];
    uint32_t firmware_address = APP_SAVE_ADDR;
    uint32_t remaining_size;
    uint8_t calculated_hash[32];
    SHA256_CTX sha_ctx;
    
    // 1. 读取固件头部
    W25Q128_read((uint8_t*)&firmware_header, firmware_address, sizeof(FirmwareHeader_t));
    
    // 2. 验证头部
    if (IAP_Verify_Firmware_Header(&firmware_header) != FIRMWARE_OK) {
        return -1;
    }
    
    // 3. 准备SHA256计算
    SHA256_Init(&sha_ctx);
    
    // 4. 逐块解密固件并计算哈希
    firmware_address += sizeof(FirmwareHeader_t);  // 跳过头部
    remaining_size = firmware_header.size;
    
    while (remaining_size > 0) {
        uint32_t block_size = (remaining_size > DECRYPT_BUFFER_SIZE) ? 
                              DECRYPT_BUFFER_SIZE : remaining_size;
        
        // 读取加密数据
        W25Q128_read(buffer, firmware_address, block_size);
        
        // 解密数据 (AES-128-CBC)
        AES_CBC_decrypt_buffer(buffer, block_size);
        
        // 更新SHA256哈希
        SHA256_Update(&sha_ctx, buffer, block_size);
        
        // 写入Flash (如果正在烧录)
        Flash_Program(FLASH_APP_ADDRESS + (firmware_address - APP_SAVE_ADDR - sizeof(FirmwareHeader_t)), 
                      buffer, block_size);
        
        firmware_address += block_size;
        remaining_size -= block_size;
    }
    
    // 5. 完成哈希计算
    SHA256_Final(calculated_hash, &sha_ctx);
    
    // 6. 比较哈希值
    if (memcmp(calculated_hash, firmware_header.sha256, 32) != 0) {
        return -2;  // 哈希验证失败
    }
    
    return 0;  // 成功
}

7. 安全增强与防护措施

7.1 防止侧信道攻击

  • 时间一致性: 固定时间比较算法防止时序攻击
  • 电源监控: 检测异常电压变化,防止故障注入
  • 去相关技术: 掩盖加密过程中的功耗特征

7.2 物理安全措施

  • 加密密钥保护:
    • 密钥分散存储
    • 禁用调试接口
    • 启用读出保护
  • 防篡改检测:
    • 监控关键参数
    • 检测异常重启
    • 记录更新尝试日志

7.3 安全启动链

实现完整的安全启动链,确保每个阶段都经过验证:

  1. 硬件信任根: 硬件唯一标识或安全元件
  2. 引导程序: 烧录保护的不可修改Bootloader
  3. 应用程序: 经过验证的可信固件
  4. 配置数据: 完整性保护的参数区域

8. 测试与验证

8.1 单元测试方案

  • 加密/解密测试: 验证加密和解密过程的正确性
  • 哈希验证测试: 确保SHA-256实现正确
  • CRC计算测试: 验证CRC32算法实现

8.2 集成测试方案

  • 完整更新流程测试: 从上位机到Bootloader的端到端测试
  • 异常情况测试:
    • 传输中断恢复
    • 电源故障恢复
    • 数据损坏检测
    • 版本回滚拒绝

8.3 安全评估

  • 已知攻击向量测试:
    • 中间人攻击
    • 回放攻击
    • 降级攻击
    • 篡改攻击
  • 渗透测试: 尝试绕过安全机制

9. 最佳实践与建议

9.1 密钥管理

  • 定期轮换密钥: 设计支持密钥更新的机制
  • 安全存储: 考虑使用安全元件或TPM存储密钥
  • 密钥分发: 建立安全的密钥分发流程

9.2 性能优化

  • 分块处理: 针对资源有限的设备,采用分块解密和验证
  • 硬件加速: 利用硬件加密引擎提高性能
  • 内存使用优化: 最小化RAM占用,特别是在解密过程中

9.3 部署与维护

  • OTA更新支持: 扩展支持无线固件更新
  • 回滚机制: 保留可回滚到已验证固件的能力
  • 日志与监控: 记录更新事件和潜在安全威胁

10. 结论

可信固件更新机制是保障嵌入式设备安全的关键组件。通过结合现代密码学技术、精心设计的验证流程和安全存储管理,可以有效防止各类固件相关攻击,确保设备运行可信代码。本文详细介绍的设计方案,覆盖了从上位机加密打包到Bootloader验证解密的完整流程,为构建安全的嵌入式系统提供了全面的技术参考。

适当的安全措施不仅保护设备免受恶意攻击,也为产品提供了更可靠的远程更新能力,降低维护成本,延长产品生命周期,是物联网和工业控制设备不可或缺的安全基础。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐