2026-06-28 04:32:18 +08:00
|
|
|
|
/*
|
2026-07-16 23:00:15 +08:00
|
|
|
|
* protocol.cpp - OTA 握手辅助实现
|
2026-04-27 03:00:48 +08:00
|
|
|
|
*
|
2026-07-16 23:00:15 +08:00
|
|
|
|
* 当前保留的接口:
|
|
|
|
|
|
* - init :硬件 RNG 填充扰码常数
|
|
|
|
|
|
* - scrambleGenerate :server nonce 生成
|
|
|
|
|
|
* - sessionKeyInit :会话密钥写入
|
|
|
|
|
|
* - sessionKeyClear :会话密钥安全擦除
|
2026-05-31 03:20:26 +08:00
|
|
|
|
*
|
2026-07-16 23:00:15 +08:00
|
|
|
|
* 帧构建、AES-ECB/CBC 加解密、CRC16 等中间件代码已迁出(见 ota_relay / spi_link_v2)。
|
2026-04-27 00:42:26 +08:00
|
|
|
|
*/
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
#include "esp_random.h"
|
|
|
|
|
|
#include "app_config.h"
|
|
|
|
|
|
#include "protocol.h"
|
2026-04-27 02:34:47 +08:00
|
|
|
|
#include "monitor/debug_log.h"
|
2026-04-27 00:42:26 +08:00
|
|
|
|
|
2026-05-31 03:20:26 +08:00
|
|
|
|
// ============================================================
|
|
|
|
|
|
// Protocol 单例
|
|
|
|
|
|
// ============================================================
|
2026-07-16 23:00:15 +08:00
|
|
|
|
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/**
|
2026-07-16 23:00:15 +08:00
|
|
|
|
* @brief 获取 Protocol 单例实例(Meyers 单例)。
|
2026-06-15 01:14:29 +08:00
|
|
|
|
* @return Protocol 单例引用。
|
|
|
|
|
|
*/
|
2026-05-31 03:20:26 +08:00
|
|
|
|
Protocol& Protocol::instance()
|
2026-04-27 00:42:26 +08:00
|
|
|
|
{
|
2026-05-31 03:20:26 +08:00
|
|
|
|
static Protocol inst;
|
|
|
|
|
|
return inst;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/**
|
2026-07-16 23:00:15 +08:00
|
|
|
|
* @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。
|
2026-06-15 01:14:29 +08:00
|
|
|
|
*/
|
2026-05-31 03:20:26 +08:00
|
|
|
|
void Protocol::init()
|
|
|
|
|
|
{
|
2026-07-16 23:00:15 +08:00
|
|
|
|
if (m_initialized) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-04-27 00:42:26 +08:00
|
|
|
|
|
2026-07-16 23:00:15 +08:00
|
|
|
|
DEBUG_INFO("PROT", "=== 初始化协议层(nonce 扰码 + 会话密钥管理)===");
|
2026-04-27 00:42:26 +08:00
|
|
|
|
|
2026-06-16 23:17:06 +08:00
|
|
|
|
esp_fill_random(m_scrambleConst, SCRAMBLE_CONST_LEN);
|
|
|
|
|
|
|
2026-07-16 23:00:15 +08:00
|
|
|
|
/* 不把完整扰码常数打到 INFO/UDP:减小日志侧信道;需要调试时开 DBG。 */
|
|
|
|
|
|
DEBUG_DBG("PROT", "SCRAMBLE_CONST ready (%u B from HW RNG)",
|
|
|
|
|
|
(unsigned)SCRAMBLE_CONST_LEN);
|
2026-06-16 23:17:06 +08:00
|
|
|
|
|
2026-05-31 03:20:26 +08:00
|
|
|
|
m_initialized = true;
|
2026-04-27 00:42:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-26 02:44:57 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 根据时间戳、地址和随机种子生成 16 字节 nonce。
|
2026-06-21 23:48:31 +08:00
|
|
|
|
* @param timestamp 当前时间戳(tick)。
|
|
|
|
|
|
* @param mem_addr 内存地址(增加随机性)。
|
|
|
|
|
|
* @param rand_seed 随机种子。
|
2026-07-16 23:00:15 +08:00
|
|
|
|
* @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()。
|
2026-05-26 02:44:57 +08:00
|
|
|
|
*/
|
2026-05-31 03:20:26 +08:00
|
|
|
|
void Protocol::scrambleGenerate(uint32_t timestamp, uint32_t mem_addr,
|
|
|
|
|
|
uint32_t rand_seed, uint8_t *nonce_out)
|
2026-04-27 00:42:26 +08:00
|
|
|
|
{
|
2026-07-16 23:00:15 +08:00
|
|
|
|
if (nonce_out == nullptr) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!m_initialized) {
|
|
|
|
|
|
init();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-27 00:42:26 +08:00
|
|
|
|
uint8_t state[16];
|
|
|
|
|
|
|
2026-07-16 23:00:15 +08:00
|
|
|
|
/* 宿主小端布局;ESP32 / GD32 均为 LE,与对端一致。 */
|
2026-04-27 00:42:26 +08:00
|
|
|
|
memcpy(&state[0], ×tamp, 4);
|
|
|
|
|
|
memcpy(&state[4], &mem_addr, 4);
|
|
|
|
|
|
memcpy(&state[8], &rand_seed, 4);
|
2026-06-16 23:17:06 +08:00
|
|
|
|
memcpy(&state[12], m_scrambleConst, 4);
|
2026-04-27 00:42:26 +08:00
|
|
|
|
|
|
|
|
|
|
for (int round = 0; round < 3; round++) {
|
|
|
|
|
|
for (int i = 0; i < 16; i++) {
|
2026-06-16 23:17:06 +08:00
|
|
|
|
state[i] ^= m_scrambleConst[i];
|
2026-07-16 23:00:15 +08:00
|
|
|
|
state[i] = (uint8_t)((state[i] << 1) | (state[i] >> 7));
|
2026-04-27 00:42:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 23:00:15 +08:00
|
|
|
|
/* 按 32-bit 字再混一轮完整 4B 扰码(memcpy 避免未对齐 uint32_t*)。 */
|
2026-04-27 00:42:26 +08:00
|
|
|
|
for (int round = 0; round < 2; round++) {
|
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
2026-07-16 23:00:15 +08:00
|
|
|
|
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);
|
2026-04-27 00:42:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
memcpy(nonce_out, state, 16);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 04:48:31 +08:00
|
|
|
|
/**
|
2026-06-21 23:48:31 +08:00
|
|
|
|
* @brief 会话密钥初始化。
|
|
|
|
|
|
* @param sess 会话上下文指针。
|
|
|
|
|
|
* @param k_session 16 字节会话密钥。
|
|
|
|
|
|
* @return 0 成功,-1 参数无效。
|
2026-07-16 23:00:15 +08:00
|
|
|
|
*
|
|
|
|
|
|
* 先 secureWipe 再拷贝,避免旧 key 残留。
|
2026-05-05 04:48:31 +08:00
|
|
|
|
*/
|
2026-05-31 03:20:26 +08:00
|
|
|
|
int Protocol::sessionKeyInit(session_ctx_t *sess, const uint8_t *k_session)
|
2026-04-27 00:42:26 +08:00
|
|
|
|
{
|
2026-07-16 23:00:15 +08:00
|
|
|
|
if (sess == nullptr || k_session == nullptr) {
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
2026-04-27 00:42:26 +08:00
|
|
|
|
|
2026-07-16 23:00:15 +08:00
|
|
|
|
secureWipe(sess, sizeof(*sess));
|
|
|
|
|
|
memcpy(sess->key, k_session, SESSION_KEY_LEN);
|
2026-04-27 00:42:26 +08:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 04:48:31 +08:00
|
|
|
|
/**
|
2026-06-28 04:32:18 +08:00
|
|
|
|
* @brief 清除会话上下文中的密钥数据。
|
2026-07-16 23:00:15 +08:00
|
|
|
|
* @param sess 会话上下文指针;为 nullptr 时忽略。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 使用 secureWipe,避免编译器优化掉「无读回的」密钥清零。
|
2026-05-05 04:48:31 +08:00
|
|
|
|
*/
|
2026-05-31 03:20:26 +08:00
|
|
|
|
void Protocol::sessionKeyClear(session_ctx_t *sess)
|
2026-04-27 00:42:26 +08:00
|
|
|
|
{
|
2026-07-16 23:00:15 +08:00
|
|
|
|
if (sess == nullptr) {
|
|
|
|
|
|
return;
|
2026-04-27 00:42:26 +08:00
|
|
|
|
}
|
2026-07-16 23:00:15 +08:00
|
|
|
|
secureWipe(sess->key, SESSION_KEY_LEN);
|
2026-06-16 23:17:06 +08:00
|
|
|
|
}
|