在MCU产品量产过程中,固件防复制和防篡改一直是开发者面临的痛点问题。传统方案要么依赖芯片自带的读保护功能(局限性大),要么采用第三方加密芯片(成本高)。这套自主开发的固件加锁系统,正是为了解决这些痛点而生。
核心设计目标很明确:
实际部署在智能锁具产线后,这套系统展现出三个突出优势:
上位机采用C#开发的WPF应用程序,核心架构包含四个关键模块:
设备通信管理层
任务调度引擎
csharp复制class TaskScheduler {
private ConcurrentQueue<DeviceTask> _queue;
private CancellationTokenSource _cts;
public void StartBatch(List<string> ports) {
_cts = new CancellationTokenSource();
Parallel.ForEach(ports, new ParallelOptions {
MaxDegreeOfParallelism = Environment.ProcessorCount * 2
}, port => {
var worker = new DeviceWorker(port);
worker.Run(_cts.Token);
});
}
}
这里有几个设计亮点:
密钥管理模块
UI反馈系统
设备端固件采用分层设计,核心安全逻辑集中在LockManager模块:
c复制typedef struct {
uint32_t magic; // 0x5A5AA5A5
uint32_t key_crc;
uint32_t timestamp;
uint16_t hw_id;
} LockFlag;
c复制enum {
STATE_IDLE,
STATE_AUTH,
STATE_RECV_KEY,
STATE_WRITE_FLASH,
STATE_VERIFY
};
void handle_states() {
static uint8_t retry_count = 0;
switch(current_state) {
case STATE_AUTH:
if(check_auth() == FAIL) {
if(++retry_count > 3)
system_reset();
}
break;
// 其他状态处理...
}
}
状态机设计带来三大好处:
上位机处理多设备并发的核心在于:
csharp复制class SerialPortWrapper {
private readonly object _lock = new object();
public AuthStatus Handshake() {
lock(_lock) {
SendCommand(CMD_HANDSHAKE);
var resp = ReadResponse(500);
return ParseStatus(resp);
}
}
}
注意要点:
实测数据:
| 并行设备数 | 平均耗时(ms) | CPU占用率 |
|---|---|---|
| 1 | 820 | 8% |
| 4 | 880 | 35% |
| 8 | 920 | 68% |
| 16 | 1100 | 92% |
设备端最关键的flash写入操作:
c复制void write_lock_flag(uint32_t key_crc) {
HAL_FLASH_Unlock();
// 先擦除后写入
FLASH_EraseInitTypeDef erase = {
.TypeErase = FLASH_TYPEERASE_PAGES,
.PageAddress = LOCK_FLAG_ADDR,
.NbPages = 1
};
uint32_t page_error;
HAL_FLASHEx_Erase(&erase, &page_error);
// 写入锁定标志
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,
LOCK_FLAG_ADDR,
0x5A5AA5A5);
// 写入密钥校验值
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,
KEY_STORE_ADDR,
key_crc);
HAL_FLASH_Lock();
}
关键防护措施:
密钥从上位机到设备端的完整旅程:
mermaid复制sequenceDiagram
上位机->>设备端: 发送加密密钥包
设备端->>设备端: 解密获得原始密钥
设备端->>设备端: 计算CRC32
设备端->>Flash: 存储CRC32值
设备端->>上位机: 返回成功响应
需要实现的三个核心接口:
c复制// 串口发送(需阻塞式实现)
void uart_send(uint8_t *data, uint16_t len) {
HAL_UART_Transmit(&huart1, data, len, 1000);
}
// Flash写入(带擦除保护)
void flash_write(uint32_t addr, uint8_t *data) {
if(IS_FLASH_PROGRAM_ADDRESS(addr)) {
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,
addr,
*(uint32_t*)data);
}
}
// Flash读取(需地址对齐)
uint32_t flash_read(uint32_t addr) {
return *(__IO uint32_t*)addr;
}
STM32系列
GD32系列
c复制#define FLASH_WAIT_STATES 2
FMC->WS = FLASH_WAIT_STATES;
ESP32系列
移植耗时参考:
| MCU型号 | 适配时间 | 主要修改点 |
|---|---|---|
| STM32F103 | 0.5h | 无 |
| GD32F303 | 1h | Flash时序调整 |
| ESP32-C3 | 2h | NVS接口实现 |
| AT32F403 | 1.5h | 库函数差异处理 |
握手失败
Flash写入错误
重复加锁报警
上位机优化
设备端优化
实测优化效果:
| 优化措施 | 单设备耗时 | 吞吐量提升 |
|---|---|---|
| 默认配置 | 820ms | - |
| DMA传输 | 760ms | 7.3% |
| 快速擦除算法 | 710ms | 13.4% |
| 异步IO+短超时 | 650ms | 20.7% |
这套系统在持续迭代中已经形成了完整的工具链,下一步计划加入云端密钥管理功能,实现生产数据可追溯。对于有更高安全需求的场景,建议结合芯片唯一ID做密钥派生,这样即使Flash内容被完整复制,也无法在其他设备上运行。