可信固件更新机制
可信固件
1. 可信固件概述
可信固件是指通过加密、认证和完整性检验等多重安全措施,确保嵌入式设备只执行经过授权和验证的固件程序。可信固件系统的目标是防止未授权修改、恶意代码注入和固件回滚等安全威胁。
1.1 可信固件的核心安全属性
- 真实性(Authenticity): 确保固件来源于可信发布者
- 完整性(Integrity): 保证固件内容在传输和存储过程中未被篡改
- 机密性(Confidentiality): 防止固件内容被未授权方获取和分析
- 新鲜度(Freshness): 防止降级和回滚攻击,确保使用最新版本
1.2 传统固件更新与可信固件更新的区别
特性 | 传统固件更新 | 可信固件更新 |
---|---|---|
传输方式 | 明文传输 | 加密传输 |
完整性检验 | 简单CRC/校验和 | 密码学哈希(SHA-256等) |
身份验证 | 无或简单密码 | 数字签名 |
防回滚机制 | 通常无 | 版本控制与验证 |
防篡改措施 | 有限 | 全面加密与签名验证 |
2. 可信固件更新系统架构
2.1 系统组件
可信固件更新系统通常包含以下核心组件:
- 上位机工具: 负责固件打包、加密、签名和传输
- 通信中间层: 负责固件数据传输(如Ymodem协议)
- 应用程序(APP): 接收加密固件并存储到外部闪存
- 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校验: 用于验证固件头部和传输数据的完整性
- 分层验证:
- 传输层使用CRC16验证包完整性
- 存储层使用CRC32验证数据完整性
- 应用层使用SHA-256验证固件真实性
4.3 版本控制与防回滚
- 版本号格式:
主版本(8位) | 次版本(8位) | 修订号(8位) | 构建号(8位)
- 版本号存储: 在外部Flash独立区域存储当前版本号
- 版本比较逻辑: 只允许安装版本号大于当前版本的固件
5. 实现流程详解
5.1 上位机固件加密与打包流程
- 编译生成原始固件
- 计算固件SHA-256哈希值
- 创建并填充固件头部:
- 设置魔术字(FIRM)
- 设置版本号
- 设置固件大小
- 写入SHA-256哈希
- 计算头部CRC32
- 使用AES-128-CBC加密固件内容
- 合并头部与加密固件数据
- 使用Ymodem协议传输完整固件包
5.2 APP程序接收与存储流程
- 初始化Ymodem接收器
- 接收固件数据包:
- 验证每个数据包的CRC16
- 临时存储到RAM缓冲区
- 写入外部Flash:
- 擦除足够的存储空间
- 写入固件数据
- 验证写入是否成功
- 接收完成后:
- 验证固件头部格式和CRC
- 检查版本号,确保大于当前版本
- 设置固件更新标志
- 重启系统,进入Bootloader
5.3 Bootloader验证与烧录流程
- 检测固件更新标志
- 从外部Flash读取固件头部
- 验证固件头部:
- 检查魔术字
- 验证头部CRC
- 检查固件大小是否合理
- 检查版本号:
- 读取当前版本号
- 比较是否允许更新
- 解密固件数据:
- 使用预设密钥解密
- 计算解密后数据的SHA-256哈希
- 与头部中存储的哈希比较
- 烧录内部Flash:
- 擦除应用程序区域
- 写入解密后的固件
- 验证写入是否成功
- 更新版本信息
- 清除更新标志
- 重启进入新应用程序
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" = 0x4649524Dif (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 安全启动链
实现完整的安全启动链,确保每个阶段都经过验证:
- 硬件信任根: 硬件唯一标识或安全元件
- 引导程序: 烧录保护的不可修改Bootloader
- 应用程序: 经过验证的可信固件
- 配置数据: 完整性保护的参数区域
8. 测试与验证
8.1 单元测试方案
- 加密/解密测试: 验证加密和解密过程的正确性
- 哈希验证测试: 确保SHA-256实现正确
- CRC计算测试: 验证CRC32算法实现
8.2 集成测试方案
- 完整更新流程测试: 从上位机到Bootloader的端到端测试
- 异常情况测试:
- 传输中断恢复
- 电源故障恢复
- 数据损坏检测
- 版本回滚拒绝
8.3 安全评估
- 已知攻击向量测试:
- 中间人攻击
- 回放攻击
- 降级攻击
- 篡改攻击
- 渗透测试: 尝试绕过安全机制
9. 最佳实践与建议
9.1 密钥管理
- 定期轮换密钥: 设计支持密钥更新的机制
- 安全存储: 考虑使用安全元件或TPM存储密钥
- 密钥分发: 建立安全的密钥分发流程
9.2 性能优化
- 分块处理: 针对资源有限的设备,采用分块解密和验证
- 硬件加速: 利用硬件加密引擎提高性能
- 内存使用优化: 最小化RAM占用,特别是在解密过程中
9.3 部署与维护
- OTA更新支持: 扩展支持无线固件更新
- 回滚机制: 保留可回滚到已验证固件的能力
- 日志与监控: 记录更新事件和潜在安全威胁
10. 结论
可信固件更新机制是保障嵌入式设备安全的关键组件。通过结合现代密码学技术、精心设计的验证流程和安全存储管理,可以有效防止各类固件相关攻击,确保设备运行可信代码。本文详细介绍的设计方案,覆盖了从上位机加密打包到Bootloader验证解密的完整流程,为构建安全的嵌入式系统提供了全面的技术参考。
适当的安全措施不仅保护设备免受恶意攻击,也为产品提供了更可靠的远程更新能力,降低维护成本,延长产品生命周期,是物联网和工业控制设备不可或缺的安全基础。