本文档说明如何将BootLoader中的AES解密和固件下载功能移植到其他STM32工程。
文件位置: BootLoaderL051/decadriver/
| 文件名 | 说明 | 必需 |
|---|---|---|
aes.c |
AES-256加密解密核心实现 | ✓ |
aes.h |
AES模块头文件 | ✓ |
功能说明:
- 实现AES-256-CBC加密解密算法
- 使用硬编码密钥 kTable(32字节)
- 主要函数:
- aesDecInit() - 解密初始化
- aesDecrypt(buffer, chainBlock) - 解密16字节数据块
- aesEncInit() - 加密初始化
- aesEncrypt(buffer, chainBlock) - 加密16字节数据块
密钥(kTable):c const unsigned char kTable[32] = { 0x21, 0x05, 0x04, 0x19, 0x89, 0x12, 0x13, 0x00, 0x18, 0xDE, 0xCA, 0x01, 0x30, 0x52, 0x01, 0x23, 0x13, 0x05, 0x33, 0x19, 0x93, 0x07, 0x08, 0x00, 0x20, 0x5B, 0xA3, 0x4A, 0x21, 0xBA, 0xF9, 0xFC };
文件位置: BootLoaderL051/decadriver/
| 文件名 | 说明 | 必需 |
|---|---|---|
ymodem.c |
Ymodem协议实现(含解密逻辑) | ✓ |
ymodem.h |
Ymodem协议头文件 | ✓ |
功能说明:
- 通过串口接收加密的固件文件
- 自动调用AES解密(第335-341行)
- 数据包CRC16校验
- 写入Flash存储
核心流程:c // 在 Ymodem_Receive() 函数中(第330-360行) 1. 接收1024字节数据包 2. 对每16字节进行AES-CBC解密: for (j = 0; j < packet_length; j += 16) { aesDecrypt(BufferIn, bufferOut); BufferIn += 16; } 3. 写入Flash
⚠️ 注意:目前缺少固件校验和验证!
文件位置: BootLoaderL051/HAL/
| 文件名 | 说明 | 必需 |
|---|---|---|
Flash.c |
Flash擦除、读写操作 | ✓ |
Flash.h |
Flash模块头文件定义 | ✓ |
功能说明:
- Flash擦除、读、写操作
- 地址和大小配置
- 写入验证
主要函数:
- FLASH_Prepare(Address, Len) - 擦除Flash
- FLASH_Write(Address, pData, Len) - 写入数据
- FLASH_Read(Address, Readbuff, Len) - 读取数据
- FLASH_If_Write() - Ymodem专用写入接口
需要修改的配置:c // 在 Flash.h 中根据实际Flash大小修改 #define USER_FLASH_END_ADDRESS 0x0800FBFF // Flash结束地址 #define APPLICATION_ADDRESS 0x8005000 // APP起始地址 #define PAGE_SIZE 0x100 // 页大小
文件位置: BootLoaderL051/decadriver/
| 文件名 | 说明 | 必需 |
|---|---|---|
menu.c |
固件下载主流程 | ✓ |
menu.h |
下载管理头文件 | ✓ |
功能说明:
- 调用Ymodem接收固件
- 下载完成后跳转到应用程序
- 错误处理
主要函数:
- SerialDownload() - 串口固件下载
- Main_Menu() - 主菜单循环
文件位置: BootLoaderL051/HAL/
| 文件名 | 说明 | 必需 |
|---|---|---|
Usart.c |
串口收发实现 | ✓ |
Usart.h |
串口头文件 | ✓ |
需要提供的接口函数:c // 在你的工程中需要实现这些函数 uint32_t SerialKeyPressed(uint8_t *key); // 检测串口是否有数据 void SerialPutChar(uint8_t c); // 发送一个字符 void SerialPutString(uint8_t *s); // 发送字符串
main.c
└─> menu.c (下载管理)
└─> ymodem.c (协议+解密)
├─> aes.c (解密算法)
├─> Flash.c (Flash操作)
└─> Usart.c (串口通信)
将以下文件复制到新工程: 新工程/ ├── AES/ │ ├── aes.c │ └── aes.h ├── Bootloader/ │ ├── ymodem.c │ ├── ymodem.h │ ├── menu.c │ └── menu.h └── HAL/ ├── Flash.c ├── Flash.h ├── Usart.c (或自行实现) └── Usart.h
USER_FLASH_END_ADDRESS - Flash结束地址APPLICATION_ADDRESS - 应用程序起始地址PAGE_SIZE - Flash页大小kTable[32] - 确保与上位机一致在你的串口模块中实现:
```c
uint32_t SerialKeyPressed(uint8_t *key) {
// 检查串口是否收到数据,如果有则读取到*key
// 返回1表示有数据,0表示无数据
}
void SerialPutChar(uint8_t c) {
// 发送一个字符
HAL_UART_Transmit(&huart1, &c, 1, 100);
}
void SerialPutString(uint8_t s) {
// 发送字符串
HAL_UART_Transmit(&huart1, s, strlen((char)s), 1000);
}
```
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
// 检查是否需要进入BootLoader
if (/* 检测条件,如按键或标志位 */) {
Main_Menu(); // 进入固件下载
}
// 否则跳转到应用程序
IAP_JumpTo(APPLICATION_ADDRESS);
while(1);
}
需要添加的代码位置: menu.c 的 SerialDownload() 函数完成后
建议实现:
```c
// 在 SerialDownload() 成功后,跳转前添加
uint32_t VerifyFirmwareChecksum(uint32_t address, uint32_t size) {
uint32_t i;
uint32_t calculated_sum = 0;
uint32_t stored_sum;
uint8_t *firmware = (uint8_t *)address;
// 计算固件数据的校验和(不包括最后4字节)
for (i = 0; i < size - 4; i++) {
calculated_sum += firmware[i];
}
calculated_sum &= 0xFFFFFFFF;
// 读取存储的校验和(小端序)
stored_sum = firmware[size - 4] |
(firmware[size - 3] << 8) |
(firmware[size - 2] << 16) |
(firmware[size - 1] << 24);
return (calculated_sum == stored_sum) ? 1 : 0;
}
```
已通过Python程序验证:
- ✓ 使用AES-256-CBC,IV为全0
- ✓ 密钥为kTable(32字节)
- ✓ 校验和正确添加在固件末尾
- ✓ 解密后数据与原始固件一致
测试命令:bash python verify_encryption.py
1. 原始固件数据(HEX转BIN)
2. 计算32位累加和(校验和)
3. 追加4字节校验和(小端序)
4. 填充至16字节倍数(0xFF填充)
5. AES-256-CBC加密(IV=全0)
6. 保存为.Bin文件
1. 通过Ymodem接收加密数据包
2. 每16字节进行AES-CBC解密
3. 写入Flash
4. (缺失)验证校验和
5. 跳转到应用程序
如有问题,请检查:
1. Flash地址配置是否正确
2. 串口波特率和配置
3. 密钥是否一致
4. HAL库版本兼容性
建议优先实现固件校验和验证功能,以提高系统可靠性!
文档创建日期: 2025-12-20
适用于: STM32L051及类似型号
工具链: Keil MDK / STM32CubeIDE