首页
/ RuView 多基站 Mesh 安全加固实战解析:从 ADR-032 看 WiFi 感知系统如何建立可信通道

RuView 多基站 Mesh 安全加固实战解析:从 ADR-032 看 WiFi 感知系统如何建立可信通道

2026-09-07 10:22:50作者:胡唯隽

导读

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

  1. TDM 同步层SyncBeacon(同步信标)与 CSI 帧格式均无消息认证,恶意节点可向 Mesh 注入伪造信标或伪造帧;
  2. CSI 帧传输:NDP 注入路径无速率限制,可被用来刷爆无线信道;
  3. 相干门控(coherence gating):重校准状态无超时上限,可能被持续干扰"焊死";
  4. 跨房间追踪:房间切换日志无界增长,长时间运行后会耗尽聚合端内存;
  5. NVS 凭据处理:凭据缓冲区用后未清零,物理内存转储可恢复 WiFi 密码;
  6. 固件并发模型: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 字节单调递增 nonce8 字节 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.h 定义的还是加固前基线:#define CSI_MAGIC 0xC5110001#define CSI_HEADER_SIZE 20CSI_MAX_FRAME_SIZE = 20 + 4×256×2csi_collector.ccsi_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_tokensndp_refill_hz 覆盖。默认值语义为:最多突发 20 次,稳态速率 20 NDP/秒

源码背景补充:csi_collector.hcsi_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.rsCrossRoomTracker 用一个无界 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.cnvs_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.cnvs_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 并打印脱敏日志。注意 ssidtarget_ip 也算敏感信息(后者指向聚合端地址),因此三项均需清零——这与 ADR 中"Apply to all three nvs_get_str call sites"的要求一致。对密码日志打 password=*** 的既有做法应继续保留。

3.7 L-5 静态可变状态的原子化访问

发现的问题csi_collector.c 用多个无同步的静态可变全局量(s_sequences_cb_counts_send_oks_send_fails_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_keysec_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_tokensndp_refill_hz 生效 集成测试:写入 NVS 值验证行为

M-5 相干门超时

ID 验收准则 测试方法
M5-1 max_stale_framesevaluate() 仍返回 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_sequences_cb_counts_send_oks_send_fail 声明为 _Atomic 代码审查
L5-2 s_hop_mutexcsi_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 还被引入以增强感知流水线:

  1. midstreamer-scheduler v0.1.0 —— 替换手工定时器 TDM 时隙调度,提供亚微秒抖动的确定性时隙触发;
  2. midstreamer-temporal-compare v0.1.0 —— 增强 ADR-030 Tier 6 的手势 DTW 匹配,提供优化 Sakoe-Chiba band DTW、LCS 与编辑距离内核;
  3. midstreamer-attractor v0.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 仓库时要点如下:

  1. 分层选型:受限节点(ESP32-S3)用"轻量手工密码学"(HMAC-SHA256 管低频高价值信标、SipHash-2-4 管高频低价值帧),聚合端(树莓派/x86)用完整 QUIC/TLS 1.3——按设备算力分配安全强度;
  2. 兼容优先security_level 三档(0 宽松 / 1 过渡 / 2 强制)配合 magic 字节版本号(0xC51100010xC5110002)实现无损灰度,任何涉及线上升级的安全方案都应照此设计;
  3. 把安全修复量化:每项发现都配套"预算数字"(15 us / 2 us / < 1 ms jitter)与可执行验收测试,避免安全加固变成不可回归的黑盒改动;
  4. 当前仓库基线提醒:仓库现存的 tdm.rs(16 字节信标、无 verify)、csi_collector.c(magic 0xC5110001、header 20 字节、非原子 s_sequence)、nvs_config.c(无清零)与 cross_room.rs(无界 Vec)等均为加固前基线;ADR-032 给出的文件级清单(本文章节 4)即针对这些具体代码点的改造路线图。集成时可对照验收标准表中的测试方法逐项核验。
登录后查看全文
热门项目推荐
相关项目推荐