Files
EmbeddedSoftwareLearn1/08-Security_Upgrade/README.md
T
2025-06-17 14:41:47 +08:00

388 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 🛡️ 第八层:安全机制与远程升级
---
### 🔹 嵌入式系统安全基础
1. 威胁模型分析
- 物理攻击:
- 探针访问调试接口(JTAG/SWD)读取 Flash 内容。
- 电压 / 时钟干扰导致程序异常(故障注入攻击)。
- 网络攻击:
- 中间人攻击(MITM)篡改通信数据。
- 恶意固件注入(利用未加密 OTA 通道)。
- 软件攻击:
- 缓冲区溢出执行恶意代码。
- 逆向工程获取算法逻辑(如加密密钥)。
2. 安全设计原则
- 最小权限原则:
每个组件仅拥有完成任务所需的最小权限(如 MPU 配置)。
- 防御纵深:
多层次安全机制(如安全启动 + 通信加密 + 运行时防护)。
- 故障安全:
系统在异常情况下自动进入安全状态(如看门狗复位)。
---
### 🔹 安全启动(Secure Boot)
> 保证启动时加载的固件是可信的
1. 基本原理
```plaintext
BootROM → 加载并验证一级Bootloader → 加载并验证二级Bootloader → 加载并验证应用固件
```
- 信任链传递:
每个阶段只信任经过上一阶段验证的代码。
2. 数字签名验证流程
```c
// 简化的签名验证伪代码
bool VerifyFirmwareSignature(uint8_t *firmware, uint32_t size, uint8_t *signature) {
// 1. 从OTP读取可信根公钥
const uint8_t *trusted_public_key = GetTrustedPublicKey();
// 2. 计算固件哈希值
uint8_t calculated_hash[32];
SHA256(firmware, size, calculated_hash);
// 3. 使用公钥解密签名获取原始哈希
uint8_t decrypted_hash[32];
RSA_PKCS1_Verify(trusted_public_key, signature, decrypted_hash);
// 4. 比较哈希值
return (memcmp(calculated_hash, decrypted_hash, 32) == 0);
}
```
3. STM32 Secure Boot 实现
- 选项字节配置:
```c
// 启用读保护(RDP)
HAL_FLASH_OB_Unlock();
FLASH_OBProgramInitTypeDef obInit = {0};
obInit.OptionType = OPTIONBYTE_RDP;
obInit.RDPLevel = OB_RDP_LEVEL_1; // 禁用调试接口
HAL_FLASHEx_OBProgram(&obInit);
HAL_FLASH_OB_Lock();
```
- TrustZone 配置(适用于 STM32L5 等支持型号):
```c
// 配置安全/非安全区域
MPU_Region_InitTypeDef MPU_InitStruct = {0};
// 配置SRAM为安全区域
MPU_InitStruct.Number = MPU_REGION_0;
MPU_InitStruct.BaseAddress = 0x20000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = DISABLE;
MPU_InitStruct.IsShareable = ENABLE;
MPU_InitStruct.IsCacheable = DISABLE;
MPU_InitStruct.IsBufferable = DISABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
```
---
### 🔹 固件加密与防逆向
1. **AES 加密固件**,防止泄露源码逻辑
- 加密流程:
- 开发阶段:使用工具链(如 GCC 插件)加密固件。
- 部署阶段:Bootloader 解密后加载到 RAM 执行。
- 密钥管理:
- 主密钥存储在 OTP(一次性可编程)区域。
- 会话密钥通过主密钥派生(如 AES-KDF)
2. Flash 读保护(RDP)
| RDP 级别 | 保护效果 | 可逆性 |
|------------|------------------------------------------|-------------------------|
| Level 0 | 无保护(默认) | 是 |
| Level 1 | 禁止调试接口,Flash 只能运行不能读取 | 降级会擦除所有 Flash |
| Level 2 | 永久禁止调试接口和 Flash 读取 | 不可逆 |
3. 代码混淆技术
- 控制流平坦化:
将线性代码转换为基于状态机的结构,增加逆向难度。
- 指令替换:
用等效指令序列替换关键操作(如a+b替换为a-(-b))。
---
### 🔹 权限隔离与防护
1. MPU(内存保护单元)配置
```c
// 配置MPU保护关键数据区
void ConfigureMPU(void) {
// 使能MPU
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
// 配置区域0保护关键代码区
MPU_Region_InitTypeDef MPU_InitStruct = {0};
MPU_InitStruct.Number = MPU_REGION_0;
MPU_InitStruct.BaseAddress = 0x08000000; // Flash起始地址
MPU_InitStruct.Size = MPU_REGION_SIZE_128KB;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_PRIV_RW_URO; // 特权可读写,用户只读
MPU_InitStruct.DisableExec = DISABLE;
MPU_InitStruct.IsShareable = DISABLE;
MPU_InitStruct.IsCacheable = DISABLE;
MPU_InitStruct.IsBufferable = DISABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
}
```
2. TrustZone 安全域隔离
- 安全资产分类:
| 类别 | 示例 | 存储位置 |
|------------|------------------------------|------------------|
| 密钥 | TLS 私钥、加密密钥 | 安全 SRAM |
| 敏感算法 | 密码验证、加密函数 | 安全代码区 |
| 安全服务 | OTA 签名验证、证书管理 | 安全任务 |
- 安全 / 非安全通信:
```c
// 从非安全代码调用安全服务
__attribute__((section(".nonsecure_call")))
uint32_t SecureService_Call(uint32_t service_id, uint32_t param1, uint32_t param2) {
// 通过SVC指令切换到安全模式
__asm("SVC #0");
// 返回值通过R0传递
}
```
---
### 🔹 OTA(Over-the-Air)升级机制
> 支持远程更新嵌入式系统的固件版本
1. 双分区升级架构
```plaintext
Flash布局:
+-------------------+ 0x08000000
| Bootloader |
+-------------------+ 0x08010000
| Application Slot A|
+-------------------+ 0x08040000
| Application Slot B|
+-------------------+ 0x08070000
| Configuration Area|
+-------------------+
```
2. 升级状态机实现
```c
typedef enum {
OTA_IDLE, // 空闲状态
OTA_CHECKING, // 检查更新
OTA_DOWNLOADING, // 下载中
OTA_VERIFYING, // 校验中
OTA_READY, // 准备重启
OTA_UPGRADING, // 升级中
OTA_FAILED // 升级失败
} OTA_State_t;
// OTA状态机处理函数
void OTA_Process(void) {
switch (ota_state) {
case OTA_IDLE:
if (check_update_flag) {
ota_state = OTA_CHECKING;
vCheckForUpdate();
}
break;
case OTA_DOWNLOADING:
if (download_complete) {
ota_state = OTA_VERIFYING;
vVerifyFirmware();
} else if (download_error) {
ota_state = OTA_FAILED;
vHandleError(DOWNLOAD_ERROR);
}
break;
// 其他状态处理...
}
}
```
3. 失败回滚机制
```c
// 启动时验证应用完整性
bool ValidateApplication(uint32_t start_address) {
// 检查向量表签名
uint32_t *vector_table = (uint32_t *)start_address;
if (vector_table[0] == 0xFFFFFFFF) { // 检查栈顶指针是否有效
return false;
}
// 计算应用哈希并验证
uint8_t calculated_hash[32];
SHA256((uint8_t *)start_address, APPLICATION_SIZE, calculated_hash);
// 从配置区获取预期哈希
uint8_t *expected_hash = GetExpectedHash();
return (memcmp(calculated_hash, expected_hash, 32) == 0);
}
// 主程序
int main(void) {
// 初始化硬件
HAL_Init();
SystemClock_Config();
// 检查主应用是否有效
if (ValidateApplication(APPLICATION_SLOT_A_ADDRESS)) {
// 跳转到主应用
JumpToApplication(APPLICATION_SLOT_A_ADDRESS);
} else if (ValidateApplication(APPLICATION_SLOT_B_ADDRESS)) {
// 主应用无效,尝试从备份应用启动
JumpToApplication(APPLICATION_SLOT_B_ADDRESS);
} else {
// 两个应用都无效,进入恢复模式
EnterRecoveryMode();
}
}
```
#### ✅ OTA 流程核心步骤
1. 检查版本更新(HTTP/MQTT 下载 manifest)
2. 下载固件(二进制)
3. 存储到备份区(Backup Slot)
4. 校验 CRC/Hash / 签名
5. 设置 Bootloader 标志位并重启
6. Bootloader 引导进入新固件
7. 若失败则回滚(Fail-safe 机制)
#### ✅ 常用升级协议
- HTTP / HTTPS
- MQTT + Base64 二进制块传输
- CoAP(轻量级)
---
### 🔹 Bootloader 开发建议
- 通用功能:下载、校验、重启、回滚
- 支持双分区升级(Slot A / Slot B)
- 防止电量中断、写失败后的砖机风险
- 可设置升级标志位(Upgrade Flag)
---
### 🔹 安全通信实践
1. TLS 握手优化
- 预共享密钥(PSK)模式:
减少证书验证开销,适合资源受限设备。
```c
// mbed TLS配置PSK
mbedtls_ssl_config_set_psk(&ssl_conf,
psk, // 预共享密钥
psk_length,
identity, // 身份标识
strlen(identity));
```
2. 证书管理方案
- 证书存储:
- 根证书存储在安全 Flash 区域。
- 设备证书通过安全通道动态更新。
- 证书验证:
```c
// 验证服务器证书链
int verify_cert(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags) {
// 检查证书有效期
if (mbedtls_x509_crt_check_validity(crt, time(NULL)) != 0) {
return MBEDTLS_ERR_X509_CERT_VERIFY_FAILED;
}
// 检查证书颁发者
if (!mbedtls_x509_crt_verify(crt, trusted_certs, NULL, NULL, flags, NULL, NULL)) {
return MBEDTLS_ERR_X509_CERT_VERIFY_FAILED;
}
return 0;
}
```
---
### 🔹 安全测试
1. 固件逆向分析
- 工具链:
- Ghidra:反编译二进制文件,生成 C 语言伪代码。
- IDA Pro:专业逆向工程工具,支持 ARM 架构。
- 防御措施:
- 固件加密:使用 AES-256 加密整个固件。
- 反调试机制:检测调试接口是否被连接。
```c
// 检测SWD/JTAG调试接口
bool IsDebuggerAttached(void) {
// 读取DBGMCU_IDCODE寄存器
uint32_t idcode = DBGMCU->IDCODE;
// 检查调试使能位
return ((DBGMCU->CR & (DBGMCU_CR_DBG_SLEEP | DBGMCU_CR_DBG_STOP | DBGMCU_CR_DBG_STANDBY)) != 0);
}
```
2. 侧信道攻击防护
- 电源分析攻击:
通过测量设备功耗分析加密密钥。
- 防护措施:
常量时间实现:避免条件分支依赖密钥值。
```c
// 常量时间比较(防止时序攻击)
bool ConstantTimeCompare(const uint8_t *a, const uint8_t *b, size_t len) {
uint8_t result = 0;
for (size_t i = 0; i < len; i++) {
result |= a[i] ^ b[i];
}
return (result == 0);
}
```
### 面试高频问题
#### 安全启动与普通启动的区别:
安全启动通过数字签名验证每个启动阶段的代码,确保只运行可信固件;普通启动无条件执行存储的代码。
#### 如何实现 OTA 升级的原子性:
使用双分区设计,升级前备份当前固件,验证新固件通过后再擦除旧固件;设置标志位记录升级状态,异常时可回滚。
#### TLS 握手过程中客户端如何验证服务器证书:
客户端使用内置根证书验证服务器证书的签名链,检查证书有效期、域名匹配和吊销状态。
#### MPU 与 TrustZone 的区别:
MPU 提供内存区域访问控制,保护不同数据段;TrustZone 在硬件层面划分安全 / 非安全区域,实现更彻底的隔离。