feat(protocol): 更新SPI Link协议至v2版本并优化CAN通信
- 将SPI OTA帧格式升级为SPI Link v2,采用新的帧格式: | SOF0 0xA5 | SOF1 0x5A | VER 0x02 | FLAGS | SESSION 4B LE | TX_SEQ | ACK_SEQ | CREDIT | TYPE | LEN 2B LE | RSV 2B | PAYLOAD | CRC32 4B LE | - 新增V2_OVERHEAD=20字节开销,APP最大payload为236字节,BOOT为108字节 - 修改帧类型定义:ENTER_BOOT从0xB5合并至0xB0,新增CAN业务类型0xC0~C3 - 更新CAN协议文档,支持SPI Link v2-only模式,调整最大业务payload计算 - 扩展0xC1帧功能,支持双向通信和1Hz主动推送ID快照 - 添加0xC3完整ID快照帧类型,包含seq/rx_count等完整字段 - 更新协议文档中的错误码定义和反馈payload格式 refactor(main): 重构主程序入口并添加CAN反馈任务 - 在main.cpp中添加CAN反馈任务,更新启动流程注释 - 创建createCriticalTask辅助函数,统一关键任务创建逻辑 - 调整SPI初始化顺序,确保CAN反馈任务依赖正确 - 优化错误处理机制,关键任务失败时立即重启 - 将GD32版本探测移至ota_relay任务处理 - 更新版本号至0.3.48,调整编译时间和Git哈希 config: 更新SPI和BLE配置参数 - 添加SPI主机相关配置参数,包括CS GPIO、READY GPIO等引脚定义 - 配置SPI缓冲区大小:APP为256字节,BOOT为128字节 - 更新BLE滑动窗口参数,ACK窗口设为32,数据队列深度为72 - 调整SPI工作队列大小至5120字节,增加任务堆栈监控说明
This commit is contained in:
+81
-31
@@ -1,18 +1,16 @@
|
||||
/*
|
||||
* protocol.cpp - 协议层实现
|
||||
* T2.1, T2.2, T1.3
|
||||
* ESP-IDF 6.0 适配版本 - 使用硬件 AES-128-CBC + 硬件 RNG
|
||||
* protocol.cpp - OTA 握手辅助实现
|
||||
*
|
||||
* 加密实现使用 ESP32-C3 硬件 AES 加速器(通过 mbedtls port)
|
||||
* 当前保留的接口:
|
||||
* - init :硬件 RNG 填充扰码常数
|
||||
* - scrambleGenerate :server nonce 生成
|
||||
* - sessionKeyInit :会话密钥写入
|
||||
* - sessionKeyClear :会话密钥安全擦除
|
||||
*
|
||||
* 当前保留的接口:init、scrambleGenerate(nonce 生成)、
|
||||
* sessionKeyInit / sessionKeyClear(会话密钥生命周期管理)。
|
||||
* 帧构建、AES-CBC 加解密、CRC16 等中间件代码已移除。
|
||||
* 帧构建、AES-ECB/CBC 加解密、CRC16 等中间件代码已迁出(见 ota_relay / spi_link_v2)。
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "esp_random.h"
|
||||
#include "aes/esp_aes.h"
|
||||
#include "app_config.h"
|
||||
#include "protocol.h"
|
||||
#include "monitor/debug_log.h"
|
||||
@@ -20,8 +18,9 @@
|
||||
// ============================================================
|
||||
// Protocol 单例
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* @brief 获取 Protocol 单例实例。
|
||||
* @brief 获取 Protocol 单例实例(Meyers 单例)。
|
||||
* @return Protocol 单例引用。
|
||||
*/
|
||||
Protocol& Protocol::instance()
|
||||
@@ -31,23 +30,45 @@ Protocol& Protocol::instance()
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化协议层,打印初始化信息并设置初始化标志。
|
||||
* @brief 抗优化密钥/敏感缓冲擦除。
|
||||
*
|
||||
* 普通 memset 可能被编译器当作「无后续读取」而消除;
|
||||
* 通过 volatile 逐字节写 0,保证运行时真正清零。
|
||||
*
|
||||
* @param p 起始地址;nullptr 忽略。
|
||||
* @param n 字节数。
|
||||
*/
|
||||
void Protocol::secureWipe(void *p, size_t n)
|
||||
{
|
||||
if (p == nullptr || n == 0U) {
|
||||
return;
|
||||
}
|
||||
volatile uint8_t *v = static_cast<volatile uint8_t *>(p);
|
||||
while (n > 0U) {
|
||||
*v++ = 0;
|
||||
--n;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化协议层,打印初始化信息并设置初始化标志。
|
||||
*
|
||||
* 使用硬件 RNG 填充 m_scrambleConst。完整常数不再以 INFO 打 hex,
|
||||
* 避免 UDP/串口日志侧信道;需要核对时开 DEBUG_DBG。
|
||||
*/
|
||||
void Protocol::init()
|
||||
{
|
||||
if (m_initialized) return;
|
||||
if (m_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_INFO("PROT", "=== 初始化协议层 ===");
|
||||
DEBUG_INFO("PROT", "协议层功能: nonce 扰码 + 会话密钥管理");
|
||||
DEBUG_INFO("PROT", "=== 初始化协议层(nonce 扰码 + 会话密钥管理)===");
|
||||
|
||||
esp_fill_random(m_scrambleConst, SCRAMBLE_CONST_LEN);
|
||||
|
||||
{
|
||||
char hex[SCRAMBLE_CONST_LEN * 2 + 1] = {0};
|
||||
for (size_t i = 0; i < SCRAMBLE_CONST_LEN; ++i)
|
||||
snprintf(&hex[i * 2], sizeof(hex) - i * 2, "%02X", m_scrambleConst[i]);
|
||||
DEBUG_INFO("PROT", "SCRAMBLE_CONST (RNG): %s", hex);
|
||||
}
|
||||
/* 不把完整扰码常数打到 INFO/UDP:减小日志侧信道;需要调试时开 DBG。 */
|
||||
DEBUG_DBG("PROT", "SCRAMBLE_CONST ready (%u B from HW RNG)",
|
||||
(unsigned)SCRAMBLE_CONST_LEN);
|
||||
|
||||
m_initialized = true;
|
||||
}
|
||||
@@ -57,13 +78,28 @@ void Protocol::init()
|
||||
* @param timestamp 当前时间戳(tick)。
|
||||
* @param mem_addr 内存地址(增加随机性)。
|
||||
* @param rand_seed 随机种子。
|
||||
* @param nonce_out 输出 16 字节 nonce 缓冲区。
|
||||
* @param nonce_out 输出 16 字节 nonce 缓冲区;为 nullptr 时直接返回。
|
||||
*
|
||||
* 混合步骤概要:
|
||||
* 1) state = timestamp|mem_addr|rand_seed|scrambleConst[0..3](小端)
|
||||
* 2) 3 轮:逐字节 XOR 全表 scrambleConst + 循环左移 1
|
||||
* 3) 2 轮:按 32-bit 字 XOR 完整 4B scrambleConst(memcpy 避免未对齐访问)
|
||||
*
|
||||
* @note 非标准 KDF;仅 OTA 握手用。未 init 时自动 init()。
|
||||
*/
|
||||
void Protocol::scrambleGenerate(uint32_t timestamp, uint32_t mem_addr,
|
||||
uint32_t rand_seed, uint8_t *nonce_out)
|
||||
{
|
||||
if (nonce_out == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (!m_initialized) {
|
||||
init();
|
||||
}
|
||||
|
||||
uint8_t state[16];
|
||||
|
||||
/* 宿主小端布局;ESP32 / GD32 均为 LE,与对端一致。 */
|
||||
memcpy(&state[0], ×tamp, 4);
|
||||
memcpy(&state[4], &mem_addr, 4);
|
||||
memcpy(&state[8], &rand_seed, 4);
|
||||
@@ -72,14 +108,22 @@ void Protocol::scrambleGenerate(uint32_t timestamp, uint32_t mem_addr,
|
||||
for (int round = 0; round < 3; round++) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
state[i] ^= m_scrambleConst[i];
|
||||
state[i] = (state[i] << 1) | (state[i] >> 7);
|
||||
state[i] = (uint8_t)((state[i] << 1) | (state[i] >> 7));
|
||||
}
|
||||
}
|
||||
|
||||
/* 按 32-bit 字再混一轮完整 4B 扰码(memcpy 避免未对齐 uint32_t*)。 */
|
||||
for (int round = 0; round < 2; round++) {
|
||||
uint32_t *words = (uint32_t *)state;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
words[i] ^= m_scrambleConst[i * 4 % 16] | ((uint32_t)m_scrambleConst[i * 4 + 1] << 8);
|
||||
uint32_t w = 0;
|
||||
memcpy(&w, &state[i * 4], 4);
|
||||
const uint32_t mix =
|
||||
(uint32_t)m_scrambleConst[i * 4]
|
||||
| ((uint32_t)m_scrambleConst[i * 4 + 1] << 8)
|
||||
| ((uint32_t)m_scrambleConst[i * 4 + 2] << 16)
|
||||
| ((uint32_t)m_scrambleConst[i * 4 + 3] << 24);
|
||||
w ^= mix;
|
||||
memcpy(&state[i * 4], &w, 4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,24 +135,30 @@ void Protocol::scrambleGenerate(uint32_t timestamp, uint32_t mem_addr,
|
||||
* @param sess 会话上下文指针。
|
||||
* @param k_session 16 字节会话密钥。
|
||||
* @return 0 成功,-1 参数无效。
|
||||
*
|
||||
* 先 secureWipe 再拷贝,避免旧 key 残留。
|
||||
*/
|
||||
int Protocol::sessionKeyInit(session_ctx_t *sess, const uint8_t *k_session)
|
||||
{
|
||||
if (!sess || !k_session) return -1;
|
||||
|
||||
memset(sess, 0, sizeof(session_ctx_t));
|
||||
memcpy(sess->key, k_session, 16);
|
||||
if (sess == nullptr || k_session == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
secureWipe(sess, sizeof(*sess));
|
||||
memcpy(sess->key, k_session, SESSION_KEY_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除会话上下文中的密钥数据。
|
||||
* @param sess 会话上下文指针。
|
||||
* @param sess 会话上下文指针;为 nullptr 时忽略。
|
||||
*
|
||||
* 使用 secureWipe,避免编译器优化掉「无读回的」密钥清零。
|
||||
*/
|
||||
void Protocol::sessionKeyClear(session_ctx_t *sess)
|
||||
{
|
||||
if (sess) {
|
||||
memset(sess->key, 0, 16);
|
||||
if (sess == nullptr) {
|
||||
return;
|
||||
}
|
||||
secureWipe(sess->key, SESSION_KEY_LEN);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user