RuView 多基站 Mesh 安全加固实战解析:从 ADR-032 看 WiFi 感知系统如何建立可信通道
导读
RuView 项目把普通 WiFi 信号转化为实时空间智能与生命体征感知,而其多基站(multistatic)协作依赖一组 ESP32 节点在同一局域网上通过 UDP 广播进行 TDM 同步与 CSI(信道状态信息)帧传输。由于这条数据通路"裸奔"于开放网络中,任何人都能伪造同步信标、注入伪造 CSI 帧,甚至瘫痪整个感知 Mesh。本篇技术文章以仓库中的 ADR-032-multistatic-mesh-security-hardening.md 为骨架,完整梳理其对 RuvSense 多基站感知栈的安全审计结论、七项加固措施的设计细节、逐文件实施计划、验收标准与 QUIC 传输层演进(ADR-032a),并结合仓库内 TDM 调度、CSI 采集、NVS 配置等源码现状进行印证。读完本文,你将掌握:受限 ESP32 设备上如何用轻量密码学(HMAC-SHA256 / SipHash-2-4)实现信标鉴权与帧完整性、如何用令牌桶限流和环形缓冲防止资源耗尽、如何做双核数据竞态修复,以及如何在"性能预算极紧"与"安全强度"之间做出工程取舍。
1. 背景:一次针对多基站感知栈的专项安全审计
1.1 ADR-032 的由来与决策上下文
ADR-032 是 RuView 系列 ADR 中针对 ADR-029(RuvSense Multistatic Sensing Mode)、ADR-030(Persistent Field Model)与 ADR-031(RuView Sensing-First RF Mode) 展开安全审计后的产物,文档头字段记录:
| 字段 | 值 |
|---|---|
| 状态 | Accepted |
| 日期 | 2026-03-01 |
| 决策者 | ruv |
| 关联 ADR | ADR-029(多基站)、ADR-030(持久场模型)、ADR-031(感知优先 RF)、ADR-018(ESP32 实现)、ADR-012(ESP32 Mesh) |
审计在以下四处核心模块发现共 7 项安全问题,按严重程度分布为:HIGH × 1、MEDIUM × 3、LOW × 3:
- TDM 同步层:
SyncBeacon(同步信标)与 CSI 帧格式均无消息认证,恶意节点可向 Mesh 注入伪造信标或伪造帧; - CSI 帧传输:NDP 注入路径无速率限制,可被用来刷爆无线信道;
- 相干门控(coherence gating):重校准状态无超时上限,可能被持续干扰"焊死";
- 跨房间追踪:房间切换日志无界增长,长时间运行后会耗尽聚合端内存;
- NVS 凭据处理:凭据缓冲区用后未清零,物理内存转储可恢复 WiFi 密码;
- 固件并发模型:CSI 采集器中的静态可变全局量被 ESP32-S3 双核无同步访问。
这些发现被归为三大类:缺少密码学认证、无界或未受保护的资源、嵌入式目标上的内存安全。
1.2 威胁模型:谁在攻击、攻击面在哪
ADR-032 定义的首要威胁行为者是同局域网内或处在 Mesh WiFi 覆盖范围内的恶意 ESP32 节点,攻击面是承载同步信标、CSI 帧与 NDP 注入的 UDP 广播平面。下表是文档给出的 STRIDE 威胁分析,其中前三行直接决定了后续密码学设计的强度选型:
| 威胁 | STRIDE | 影响 | 可利用性 |
|---|---|---|---|
| 伪造 SyncBeacon 注入 | Spoofing、Tampering | 整个 Mesh 失去同步、完全无姿态输出 | 低技能,LAN 上放一个 rogue ESP32 |
| CSI 帧伪造 | Spoofing、Tampering | 姿态估计被污染、出现"幽灵"占用者 | 低技能,UDP 包注入 |
| NDP RF 洪泛 | DoS | 信道饱和、CSI 数据丢失 | 低技能,重复调用 NDP |
| 相干门停滞 | DoS | 无限重校准、输出冻结 | 需持续干扰 |
| 切换日志耗尽 | DoS | 长期运行后聚合端 OOM | 被动,无需攻击者 |
| 凭据栈残留 | 信息泄露 | RAM dump 可恢复 WiFi 密码 | 需物理接触设备 |
| 双核数据竞态 | Tampering、DoS | CSI 帧损坏、未定义行为 | 被动,无需攻击者 |
1.3 设计约束:为什么不能直接上 TLS
加固方案必须在非常苛刻的嵌入式资源约束下设计。ADR-032 明确列出了以下几项硬约束,这与仓库中 TDM 调度实现 的时序设计是一致的:
- 1 ms 保护间隔预算:ESP32-S3 的 CPU 预算有限,所有密码学运算必须落在 TDM 时隙间 1 ms 的 guard interval 内完成。
- HMAC-SHA256 成本:借助 mbedtls 硬件加速,在 ESP32-S3 上对 24 字节载荷约 15 us,预算内绰绰有余。
- SipHash-2-4 成本:对 64 字节载荷约 2 us,适合逐帧 MAC。
- 无 TCP/TLS:感知数据通路为低延迟 UDP 广播,不提供 TLS/TCP。
- PSK 模型可接受:同一 Mesh 部署中的所有节点由同一运维者配置,可接受预共享密钥模型。
仓库佐证:在 tdm.rs 中可以看到默认 4 节点 20 Hz 的 TDM 调度:
4 × (4 ms TX 时隙 + 1 ms guard) + 30 ms 处理窗 = 50 ms,每时隙间保留 1 ms guard;该文件同时计算了 ESP32 晶振 ±10 ppm 漂移在 50 ms 周期内约 0.5 us,说明 guard interval 预算十分宽裕,这为信标鉴权留下了可验证的时序余量。
2. 决策总览:六项加固 + 兼容窗口
ADR-032 的核心决策是:用六类措施加固多基站 Mesh——信标鉴权、帧完整性、NDP 限流、有界缓冲、内存安全、密钥管理。所有变更向后兼容:在 security_level NVS 参数控制的迁移窗口期内,未认证帧仍会被接受。
也就是说,方案并非"一刀切拒绝旧帧",而是提供三档灰度策略(详见 2.8 节),让存量部署能平滑升级,避免"白天全网断服"。下面逐一展开各项设计。
3. 七项加固措施逐条解析
3.1 H-1 信标鉴权协议:16 字节 → 28 字节的 SyncBeacon
发现的问题:当前 16 字节的 SyncBeacon 线格式没有加密认证,恶意节点可以注入假信标让 TDM Mesh 整体失去同步。该发现对应代码中的 SyncBeacon 序列化/反序列化(to_bytes() 输出 [u8; 16],from_bytes() 要求至少 16 字节),文档注释中亦标注其线格式为"planned",即尚未引入认证。
解决方案:把 SyncBeacon 线格式扩展为 28 字节——新增 4 字节单调递增 nonce 与 8 字节 HMAC-SHA256 截断标签:
Authenticated SyncBeacon wire format (28 bytes):
[0..7] cycle_id (LE u64)
[8..11] cycle_period_us (LE u32)
[12..13] drift_correction (LE i16)
[14..15] reserved
[16..19] nonce (LE u32, monotonically increasing)
[20..27] hmac_tag (HMAC-SHA256 truncated to 8 bytes)
HMAC 计算方式:
key = 16-byte pre-shared mesh key (stored in NVS, namespace "mesh_sec")
message = beacon[0..20] (first 20 bytes: payload + nonce)
tag = HMAC-SHA256(key, message)[0..8] (truncated to 8 bytes)
Nonce 与重放保护是这套设计里容易被忽略但非常关键的部分:
- 协调者(coordinator)维护一个每信标自增的 32 位单调 nonce;
- 每个接收者按发送者维护
last_accepted_nonce,仅当nonce > last_accepted_nonce - REPLAY_WINDOW时接受信标,其中REPLAY_WINDOW = 16(为 UDP 乱序留的窗口); - nonce 溢出(20 Hz 下 2^32 个信标约需 6.8 年)会触发强制密钥轮换。
落地位置:tdm.rs 中扩展 SyncBeacon::to_bytes() / SyncBeacon::from_bytes() 以生成/消费 28 字节认证格式,并新增 SyncBeacon::verify() 方法。
源码背景补充:该文件中
TdmCoordinator::begin_cycle()目前已经实现了严格的 cycle_id 单调递增(首个周期为 0,之后每次自增),并计算负的累计漂移校正值作为drift_correction_us——H-1 的 nonce 单调性设计可直接复用同一状态机模式;文档给出的验收测试(调用begin_cycle()100 次验证严格单调)与该模块现有测试风格完全吻合。
3.2 M-3 CSI 帧完整性:逐帧 SipHash-2-4 标签
发现的问题:ADR-018 定义的 CSI 帧格式没有任何密码学 MAC,帧在传输途中可被伪造或篡改。
解决方案:在 CSI 帧头追加 8 字节 SipHash-2-4 标签。之所以选 SipHash 而不是 HMAC-SHA256,是因为它对短消息在 ESP32 上快约 7 倍(约 2 us vs 15 us),而对"非机密数据"提供足够完整性——CSI 数据本就不加密,关键是防篡改。
Extended CSI frame header (28 bytes, was 20):
[0..3] Magic: 0xC5110002 (bumped from 0xC5110001 to signal auth)
[4] Node ID
[5] Number of antennas
[6..7] Number of subcarriers (LE u16)
[8..11] Frequency MHz (LE u32)
[12..15] Sequence number (LE u32)
[16] RSSI (i8)
[17] Noise floor (i8)
[18..19] Reserved
[20..27] siphash_tag (SipHash-2-4 over [0..20] + IQ data)
SipHash 密钥派生(启动时一次性从 mesh key 派生并缓存于内存):
siphash_key = HMAC-SHA256(mesh_key, "csi-frame-siphash")[0..16]
落地位置:
- csi_collector.c —— 在
csi_serialize_frame()中计算 SipHash 标签并提升 magic 常量; - crates(硬件层) —— 在聚合端帧解析器加入帧校验。
源码背景补充:当前仓库中 csi_collector.h 定义的还是加固前基线:
#define CSI_MAGIC 0xC5110001、#define CSI_HEADER_SIZE 20,CSI_MAX_FRAME_SIZE = 20 + 4×256×2。csi_collector.c 的csi_serialize_frame()内按CSI_HEADER_SIZE + iq_len组织帧并通过memcpy写入 LE magic、序列号(取自自增的s_sequence++)。ADR-032 的目标正是把 header 从 20 字节扩到 28 字节、magic 升为0xC5110002,同时保持 IQ 数据段不变,因此对现有帧体的改动最小。
3.3 M-4 NDP 注入令牌桶限流
发现的问题:csi_inject_ndp_frame() 没有速率限制,无节制的 NDP 注入会淹没 RF 信道,形成对共享无线介质的 DoS。
解决方案:引入带 NVS 可配置参数的令牌桶(token bucket)限流器:
// Token bucket parameters (defaults)
#define NDP_RATE_MAX_TOKENS 20 // burst capacity
#define NDP_RATE_REFILL_HZ 20 // sustained rate: 20 NDP/sec
#define NDP_RATE_REFILL_US (1000000 / NDP_RATE_REFILL_HZ)
typedef struct {
uint32_t tokens; // current token count
uint32_t max_tokens; // bucket capacity
uint32_t refill_interval_us; // microseconds per token
int64_t last_refill_us; // last refill timestamp
} ndp_rate_limiter_t;
桶空时 csi_inject_ndp_frame() 返回 ESP_ERR_NOT_ALLOWED。速率参数可用 NVS 键 ndp_max_tokens 与 ndp_refill_hz 覆盖。默认值语义为:最多突发 20 次,稳态速率 20 NDP/秒。
源码背景补充:csi_collector.h 中
csi_inject_ndp_frame()的文档说明它走esp_wifi_80211_tx()发送仅 preamble 的空数据帧(约 24 us 空中占用时间),是 ADR-029"感知优先 TX"机制;当前实现注释还标注了 TODO(placeholder 帧)。这解释了为何必须在固件侧加限流——一次调用即产生一次真实 RF 发射,且该调用路径位于普通应用代码可达范围内。
3.4 M-5 相干门重校准超时:ForcedAccept 保输出可用
发现的问题:coherence_gate.rs 中的 Recalibrate 状态可被无限期持有。持续干扰源能让系统永远处于重校准状态,导致完全无输出。仓库当前实现中,GateDecision 枚举只有 Accept / PredictOnly / Reject / Recalibrate 四个变体,且 GatePolicy::evaluate() 在 stale_count >= max_stale_frames(默认 200 帧,约 10 s@20Hz)时就进入 Recalibrate——恰好缺少"重校准自身超时"的保护,这正是 M-5 要补的洞。
解决方案:向 GatePolicyConfig 增加可配置的 max_recalibrate_duration(默认 30 秒 = 600 帧 @20 Hz)。当重校准持续时间超过上限时,门控进入 ForcedAccept 状态,并以 10 倍膨胀噪声接受数据,保证"降级但可用"的输出。
pub enum GateDecision {
Accept { noise_multiplier: f32 },
PredictOnly,
Reject,
Recalibrate { stale_frames: u64 },
/// Recalibration timed out. Accept with heavily inflated noise.
ForcedAccept { noise_multiplier: f32, stale_frames: u64 },
}
新增配置字段:
pub struct GatePolicyConfig {
// ... existing fields ...
/// Maximum frames in Recalibrate before forcing accept. Default: 600 (30s at 20Hz).
pub max_recalibrate_frames: u64,
/// Noise multiplier for ForcedAccept. Default: 10.0.
pub forced_accept_noise: f32,
}
落地位置:coherence_gate.rs 扩展 GateDecision 枚举并修改 GatePolicy::evaluate()。
设计意图解读:Kalman 更新中"高噪声 × 降级接受"优于"零输出"。10 倍噪声膨胀会让融合结果变"发散但稳定跟随",在持续干扰下保住系统可用性底线,同时 30 s 上限保证干扰一旦停止系统能快速恢复。
3.5 L-1 有界切换日志:Vec → VecDeque 环形缓冲
发现的问题:cross_room.rs 中 CrossRoomTracker 用一个无界 Vec<TransitionEvent> 保存跨房间切换事件,长时间运行(数天/数周)会无限增长。当前仓库实现正是如此——transitions: Vec<TransitionEvent>,match_entry() 里 self.transitions.push(...),无任何容量上限。
解决方案:把 Vec 换成 VecDeque 环形缓冲,容量满时逐出最旧条目。同时保留"追加型审计日志"语义:事件永不修改,只按年龄被逐出。
pub struct CrossRoomConfig {
// ... existing fields ...
/// Maximum transitions retained in the ring buffer. Default: 1000.
pub max_transitions: usize,
}
实现策略:transitions.len() >= max_transitions 时先 pop_front() 再 push。当前 cross_room.rs 中已存在的 pending_exits 淘汰逻辑(满则 remove(0))可作为同一模式参照。
3.6 L-4 NVS 密码缓冲区清零
发现的问题:nvs_config.c 的 nvs_config_load() 用栈缓冲区 buf 读取 WiFi 密码,用后未清零。ESP32-S3 的栈内存在执行返回后不会自动清除,物理内存转储可恢复凭据。
解决方案:每次 NVS 字符串读取后,用 explicit_bzero() 清零栈缓冲(ESP-IDF 通过 newlib 提供);若不可用,则用 volatile 指针配合 memset 防止编译器优化掉清零。
/* After each nvs_get_str that may contain credentials: */
explicit_bzero(buf, sizeof(buf));
/* Portable fallback: */
static void secure_zero(void *ptr, size_t len) {
volatile unsigned char *p = (volatile unsigned char *)ptr;
while (len--) { *p++ = 0; }
}
落地位置:nvs_config.c 中 nvs_config_load() 的全部三个 nvs_get_str 调用点(ssid、password、target_ip)之后都要加 explicit_bzero(buf, sizeof(buf))。
源码背景补充:仓库当前 nvs_config.c 确实用同一个
char buf[NVS_CFG_PASS_MAX]依次读取ssid/password/target_ip,且password命中后会拷贝到cfg->wifi_password并打印脱敏日志。注意ssid、target_ip也算敏感信息(后者指向聚合端地址),因此三项均需清零——这与 ADR 中"Apply to all three nvs_get_str call sites"的要求一致。对密码日志打password=***的既有做法应继续保留。
3.7 L-5 静态可变状态的原子化访问
发现的问题:csi_collector.c 用多个无同步的静态可变全局量(s_sequence、s_cb_count、s_send_ok、s_send_fail、s_hop_index),被 ESP32-S3 的两个核并发访问:CSI 回调运行在 WiFi 任务(默认钉在 core 0),而主应用与跳频定时器可能跑在 core 1。仓库当前代码(如 static uint32_t s_sequence = 0;)正是竞态源头。
解决方案:所有共享计数器加 C11 _Atomic 限定符;跳频表状态因涉及多变量一致性,改用 FreeRTOS 互斥锁保护。
#include <stdatomic.h>
static _Atomic uint32_t s_sequence = 0;
static _Atomic uint32_t s_cb_count = 0;
static _Atomic uint32_t s_send_ok = 0;
static _Atomic uint32_t s_send_fail = 0;
static _Atomic uint8_t s_hop_index = 0;
/* Hop table protected by mutex (multi-variable consistency) */
static SemaphoreHandle_t s_hop_mutex = NULL;
互斥锁在 csi_collector_init() 中创建;csi_hop_next_channel() 的读与 csi_collector_set_hop_table() 的写在取/放 s_hop_mutex 的保护下进行。选择"单计数器用原子量、多变量复合状态用互斥锁"的分层策略,是为了在锁开销(最多约 1 us 的跳频路径延迟,仍在 guard interval 预算内)与一致性需求之间取得平衡。
3.8 密钥管理:单一 16 字节预共享密钥 + 三档灰度
所有密码学操作共享一个存储在 NVS 中的 16 字节预共享 mesh key。
预置(Provisioning):
NVS namespace: "mesh_sec"
NVS key: "mesh_key"
NVS type: blob (16 bytes)
密钥在节点设置阶段通过现有 scripts/provision.py 工具下发——该工具被扩展为:为一次部署生成随机 16 字节密钥并烧录到全部节点,同时新增 rotate-key 管理命令。
密钥派生:
beacon_hmac_key = mesh_key (direct, 16 bytes)
frame_siphash_key = HMAC-SHA256(mesh_key, "csi-frame-siphash")[0..16] (derived, 16 bytes)
密钥轮换:
- 手动轮换:
provision.py rotate-key --deployment <id>; - 协调者广播密钥轮换事件(用旧密钥签名),事件携带用旧密钥加密的新密钥;
- 节点接受新密钥后,待确认下一个信标已用新密钥签名,才正式切换;
- 建议每 90 天轮换一次,或任何节点退役后立即轮换。
兼容窗口(security_level NVS 参数):
NVS key: "sec_level"
Values:
0 = permissive (accept unauthenticated frames, log warning)
1 = transitional (accept both authenticated and unauthenticated)
2 = enforcing (reject unauthenticated frames)
Default: 1 (transitional, for backward compatibility during rollout)
部署指引解读:默认
1(过渡态)意味着混合新旧节点的部署在升级期间照常工作——旧固件节点发 16 字节无认证信标仍被接受,新节点间则已启用认证;待全网升级完成后把sec_level调到2(强制)才能真正发挥 H-1/M-3 的防护价值。若想先观察告警,可设为0(宽松 + 日志告警)。
4. 逐文件实施计划(三阶段 + 内存安全)
ADR-032 将全部改动按依赖关系分为四个阶段,每项标出优先级(P0/P1/P2)。下表完整继承自文档:
阶段 1:信标鉴权与密钥管理
| 文件 | 变更 | 优先级 |
|---|---|---|
| v2/crates/wifi-densepose-hardware/src/esp32/tdm.rs | 扩展 SyncBeacon 为 28 字节认证格式,新增 verify()、nonce 跟踪与重放窗口 |
P0 |
| firmware/esp32-csi-node/main/nvs_config.c | 新增 mesh_key、sec_level 的 NVS 读取 |
P0 |
| firmware/esp32-csi-node/main/nvs_config.h | 在 nvs_config_t 中新增 mesh_key[16]、sec_level |
P0 |
| scripts/provision.py | 新增 --mesh-key 生成与 rotate-key 命令 |
P0 |
阶段 2:帧完整性与速率限制
| 文件 | 变更 | 优先级 |
|---|---|---|
| firmware/esp32-csi-node/main/csi_collector.c | 帧序列化加 SipHash-2-4 标签、NDP 令牌桶限流、_Atomic 限定符、跳频互斥锁 |
P1 |
| firmware/esp32-csi-node/main/csi_collector.h | CSI_HEADER_SIZE 更新为 28,新增限流配置 |
P1 |
| v2/crates/wifi-densepose-hardware/src/esp32/ | 聚合端解析器加入帧校验 | P1 |
阶段 3:有界缓冲与门控加固
| 文件 | 变更 | 优先级 |
|---|---|---|
| v2/crates/wifi-densepose-signal/src/ruvsense/cross_room.rs | Vec 替换为 VecDeque,新增 max_transitions 配置 |
P1 |
| v2/crates/wifi-densepose-signal/src/ruvsense/coherence_gate.rs | 新增 ForcedAccept 变体与 max_recalibrate_frames 配置 |
P1 |
阶段 4:内存安全收尾
| 文件 | 变更 | 优先级 |
|---|---|---|
| firmware/esp32-csi-node/main/nvs_config.c | 凭据读取后加 explicit_bzero() |
P2 |
| firmware/esp32-csi-node/main/csi_collector.c | _Atomic 计数器、s_hop_mutex(若阶段 2 未完成) |
P2 |
5. 验收标准:把每项修复变成可执行测试
ADR-032 为每个发现都定义了带 ID、验收准则与测试方法的表格,是理解"什么叫真正修完"的关键。以下按原文整理并补充测试思路说明。
H-1 信标鉴权
| ID | 验收准则 | 测试方法 |
|---|---|---|
| H1-1 | SyncBeacon::to_bytes() 产出带有效 HMAC 标签的 28 字节输出 |
单元测试:序列化后用重算 HMAC 校验标签匹配 |
| H1-2 | SyncBeacon::verify() 拒绝 HMAC 标签错误的信标 |
单元测试:翻转标签 1 个比特,verify 返回 Err |
| H1-3 | SyncBeacon::verify() 拒绝窗口外重放 nonce |
单元测试:提交 nonce = last_accepted - REPLAY_WINDOW - 1,验证被拒 |
| H1-4 | SyncBeacon::verify() 接受窗口内 nonce |
单元测试:提交 nonce = last_accepted - REPLAY_WINDOW + 1,验证通过 |
| H1-5 | 协调者 nonce 跨周期严格单调 | 单元测试:调用 begin_cycle() 100 次验证严格单调 |
| H1-6 | 向后兼容:sec_level=0 接受无认证 16 字节信标 |
集成测试:新旧节点混合部署 |
M-3 帧完整性
| ID | 验收准则 | 测试方法 |
|---|---|---|
| M3-1 | magic 为 0xC5110002 的 CSI 帧含有效 8 字节 SipHash 标签 |
单元测试:序列化帧并校验标签 |
| M3-2 | 帧校验拒绝被篡改的 IQ 数据 | 单元测试:翻转 IQ 载荷 1 字节,验证被拒 |
| M3-3 | ESP32-S3 上 SipHash 计算耗时 < 10 us | 目标硬件基准测试 |
| M3-4 | sec_level < 2 时解析器接受旧 magic 0xC5110001 |
单元测试:向后兼容 |
M-4 NDP 限流
| ID | 验收准则 | 测试方法 |
|---|---|---|
| M4-1 | 前 max_tokens 次调用 csi_inject_ndp_frame() 全部成功 |
单元测试:快速调用 20 次 |
| M4-2 | 桶空时第 21 次调用返回 ESP_ERR_NOT_ALLOWED |
单元测试:耗尽令牌桶 |
| M4-3 | 桶按配置速率补充 | 单元测试:耗尽后等 refill_interval_us,验证可补 1 个令牌 |
| M4-4 | NVS 覆盖 ndp_max_tokens、ndp_refill_hz 生效 |
集成测试:写入 NVS 值验证行为 |
M-5 相干门超时
| ID | 验收准则 | 测试方法 |
|---|---|---|
| M5-1 | max_stale_frames 处 evaluate() 仍返回 Recalibrate |
单元测试:既有行为保持 |
| M5-2 | max_recalibrate_frames 处返回 ForcedAccept |
单元测试:喂入 max_recalibrate_frames + 1 个低相干帧 |
| M5-3 | ForcedAccept 的噪声乘数等于 forced_accept_noise(默认 10.0) |
单元测试:校验字段 |
| M5-4 | 默认 max_recalibrate_frames = 600 |
单元测试:校验默认配置 |
L-1 有界切换日志
| ID | 验收准则 | 测试方法 |
|---|---|---|
| L1-1 | transition_count() 永不超过 max_transitions |
单元测试:max_transitions=1000 下插入 1500 条,验证计数为 1000 |
| L1-2 | 最旧条目先被逐出(FIFO) | 单元测试:验证首条为第 (N-999) 条插入记录 |
| L1-3 | 默认 max_transitions = 1000 |
单元测试:校验默认配置 |
L-4 密码清零
| ID | 验收准则 | 测试方法 |
|---|---|---|
| L4-1 | 每次 nvs_get_str 后栈缓冲 buf 已清零 |
代码审查 + 静态分析(无法运行时测试) |
| L4-2 | 使用 explicit_bzero(而非裸 memset)防编译器优化 |
代码审查:验证调用为 explicit_bzero 或 volatile 指针模式 |
L-5 原子静态状态
| ID | 验收准则 | 测试方法 |
|---|---|---|
| L5-1 | s_sequence、s_cb_count、s_send_ok、s_send_fail 声明为 _Atomic |
代码审查 |
| L5-2 | s_hop_mutex 在 csi_collector_init() 中创建 |
代码审查 + 集成测试:初始化成功 |
| L5-3 | csi_hop_next_channel() 与 csi_collector_set_hop_table() 取/放互斥锁 |
代码审查 |
| L5-4 | ThreadSanitizer 下无数据竞争 | Rust 侧 host 构建跑 cargo test(TSAN);C 侧用 QEMU 或硬件测试 |
值得注意的测试分层策略:L-4/L-5 这类嵌入式内存安全问题大多无法做运行时断言,ADR 明确采用"代码审查 + 静态分析 + host 侧 TSAN"的组合;而能单测的逻辑(HMAC 标签、nonce 窗口、令牌桶、环形缓冲逐出)则全部下沉为精确的单元/集成测试。这保证了修复的可验证性与回归可控性。
6. 影响与代价分析
ADR-032 用专节量化了加固的正反面影响,可直接作为工程评审依据。
正向收益
- 防 rogue 节点:HMAC 认证信标阻止未授权节点让 Mesh 失步;
- 帧完整性:SipHash MAC 可检出 CSI 数据在途篡改,阻止"幽灵占用者"注入;
- RF 可用性:令牌桶限流阻止 NDP 洪泛耗尽共享无线介质;
- 内存有界:切换日志环形缓冲 + 重校准超时上限,杜绝长运行资源耗尽;
- 凭据卫生:缓冲区清零缩短物理内存访问下的凭据恢复窗口;
- 线程安全:原子操作与互斥锁消除 ESP32-S3 双核未定义行为;
- 向后兼容:
sec_level参数支持渐进式灰度升级,不打断存量部署。
代价
- SyncBeacon 增加 12 字节:28 vs 16 字节(增加 75%,但单 UDP 包内仍有富余);
- CSI 帧头增加 8 字节:28 vs 20 字节(header 增 40%,相对 128–512 字节的 IQ 载荷可忽略);
- CPU 开销:HMAC-SHA256 每个信标约 15 us(每 50 ms 周期一次 = 0.03% CPU);SipHash 每帧约 2 us(100 Hz 下 = 0.02% CPU);
- 密钥管理复杂度:mesh key 需下发所有节点并定期轮换;密钥丢失需重新预置全部节点;
- 互斥锁竞争:跳频表互斥锁最多给跳频路径增加约 1 us 延迟,在 guard interval 预算内。
残余风险与缓解
| 风险 | 概率 | 影响 | 缓解 |
|---|---|---|---|
| 老款(非 S3)ESP32 上 HMAC 计算超出 guard interval | 低 | 旧硬件无法启用信标认证 | 全系 ESP32 均有硬件加速 SHA256,基准确认 < 50 us |
| ESP32 侧信道导致密钥泄露 | 极低 | Mesh 认证整体失效 | 密钥存 eFuse(ESP32-S3 支持)或加密 NVS 分区 |
| ForcedAccept 产出不可接受的噪声姿态 | 中 | 持续干扰期间姿态质量降级 | 10 倍噪声乘数可配置,运维可调大或禁用 |
| SipHash 碰撞(64 位标签) | 极低 | 单帧伪造被接受 | 每帧 2^-64 概率,攻击者无法以协议速率遍历 |
7. ADR-032a:QUIC 传输层演进(Amendment)
ADR-032 的第 6 节补充(Amendment)是一个重要演进:手工密码学栈虽然正确高效,但存在运维层面的硬伤——手工密钥轮换需自定义协议、纯 UDP 无拥塞控制、节点漫游需手动重连、自定义 nonce 与 QUIC 内建重放保护重复造轮子。
决策:聚合端上行改用 midstreamer-quic
对具备充足 CPU 与内存的聚合级节点(Raspberry Pi、x86 网关),用 midstreamer-quic v0.1.0 替代手工密码学层。两者能力对比如下:
| 能力 | 手工方案(原 ADR-032) | QUIC(midstreamer-quic) |
|---|---|---|
| 认证 | HMAC-SHA256 截断 8B | TLS 1.3 AEAD(AES-128-GCM) |
| 帧完整性 | SipHash-2-4 标签 | QUIC 包级 AEAD |
| 重放保护 | 手工 nonce + 窗口 | QUIC 包号(单调) |
| 密钥轮换 | 自定义协调者广播 | TLS 1.3 KeyUpdate 消息 |
| 拥塞控制 | 无 | QUIC cubic/BBR |
| 连接迁移 | 不支持 | QUIC Connection ID 迁移 |
| 多流 | N/A | QUIC streams(beacon、CSI、control) |
受限设备(ESP32-S3)保留第 2.1–2.2 节的手工密码学路径作为回退。SecurityMode 枚举选择传输层:
pub enum SecurityMode {
/// Manual HMAC/SipHash over plain UDP (ESP32-S3, ADR-032 original).
ManualCrypto,
/// QUIC transport with TLS 1.3 (aggregator-class nodes).
QuicTransport,
}
QUIC 流映射(按优先级分流)
三条专用 QUIC 流按流量优先级隔离:
| Stream ID | 用途 | 方向 | 优先级 |
|---|---|---|---|
| 0 | 同步信标 | 协调者 → 节点 | 最高(TDM 时序关键) |
| 1 | CSI 帧 | 节点 → 聚合端 | 高(感知数据) |
| 2 | 控制平面 | 双向 | 普通(配置、密钥轮换、健康) |
三条配套 midstreamer 集成
除 QUIC 传输外,三个 midstreamer crate 还被引入以增强感知流水线:
midstreamer-schedulerv0.1.0 —— 替换手工定时器 TDM 时隙调度,提供亚微秒抖动的确定性时隙触发;midstreamer-temporal-comparev0.1.0 —— 增强 ADR-030 Tier 6 的手势 DTW 匹配,提供优化 Sakoe-Chiba band DTW、LCS 与编辑距离内核;midstreamer-attractorv0.1.0 —— 增强 ADR-030 Tier 4 的纵向漂移检测,用动力系统分析提前发现相空间吸引子位移(在普通指标漂移显形之前预示生物力学状态迁移)。
回退策略:加法而非替换
- ESP32-S3 节点:继续用手工 HMAC/SipHash over UDP(缺内存跑不下完整 TLS 1.3 栈);
- 聚合端节点:默认用
midstreamer-quic,QUIC 握手失败(如网络分区)时回退手工密码学; - 混合部署:聚合端自动识别入站连接——按 TLS ClientHello 判定 QUIC、按 magic 字节判定纯 UDP——再路由到相应处理路径。
QUIC 验收标准
| ID | 验收准则 | 测试方法 |
|---|---|---|
| Q-1 | 两节点 100ms 内建立 QUIC 连接 | 集成测试:握手计时 |
| Q-2 | 信标流送达抖动 < 1ms | 单元测试:发 1000 信标测到达间隔方差 |
| Q-3 | CSI 流吞吐 ≥ 纯 UDP 的 95% | 基准:criterion 对比 |
| Q-4 | 模拟 IP 变更后连接迁移成功 | 集成测试:rebind 验证流连续性 |
| Q-5 | QUIC 不可用时回退手工密码学 | 单元测试:拒绝 QUIC 验证 ManualCrypto 路径 |
| Q-6 | ManualCrypto 线格式与原 ADR-032 逐字节一致 |
单元测试:字节级对比 |
8. 与相关 ADR 的关系
| ADR | 关系 |
|---|---|
| ADR-029(RuvSense Multistatic) | 被加固:TDM 信标与 CSI 帧认证、NDP 限流、QUIC 传输 |
| ADR-030(Persistent Field Model) | 被保护:相干门超时;切换日志有界;手势 DTW 增强(midstreamer-temporal-compare);漂移检测增强(midstreamer-attractor) |
| ADR-031(RuView RF Mode) | 被加固:认证信标经 QUIC 流保护跨视点同步 |
| ADR-018(ESP32 Implementation) | 被扩展:CSI 帧头升到 v2 并带 SipHash 标签;magic 向后兼容检查 |
| ADR-012(ESP32 Mesh) | 被加固:Mesh 密钥管理、NVS 凭据清零、固件原子状态、QUIC 连接迁移 |
9. 给实施与集成者的小结
从 ADR-032 全文可以看到一套值得借鉴的嵌入式安全工程方法论,落实到 RuView 仓库时要点如下:
- 分层选型:受限节点(ESP32-S3)用"轻量手工密码学"(HMAC-SHA256 管低频高价值信标、SipHash-2-4 管高频低价值帧),聚合端(树莓派/x86)用完整 QUIC/TLS 1.3——按设备算力分配安全强度;
- 兼容优先:
security_level三档(0 宽松 / 1 过渡 / 2 强制)配合 magic 字节版本号(0xC5110001→0xC5110002)实现无损灰度,任何涉及线上升级的安全方案都应照此设计; - 把安全修复量化:每项发现都配套"预算数字"(15 us / 2 us / < 1 ms jitter)与可执行验收测试,避免安全加固变成不可回归的黑盒改动;
- 当前仓库基线提醒:仓库现存的 tdm.rs(16 字节信标、无 verify)、csi_collector.c(magic
0xC5110001、header 20 字节、非原子s_sequence)、nvs_config.c(无清零)与 cross_room.rs(无界Vec)等均为加固前基线;ADR-032 给出的文件级清单(本文章节 4)即针对这些具体代码点的改造路线图。集成时可对照验收标准表中的测试方法逐项核验。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0626
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00