首页
/ RuView 传感器身份认证与射频证据链追踪:ADR-305 签名测量信封协议及 ruview-attest 实现解析

RuView 传感器身份认证与射频证据链追踪:ADR-305 签名测量信封协议及 ruview-attest 实现解析

2026-09-08 11:45:16作者:韦蓉瑛

本文以 RuView 仓库中的架构决策记录 ADR-305: Authenticated sensor identity — RF chain of custody 为骨架,系统讲解 RuView 如何为基于商用 WiFi 的 CSI/CIR 射频感知引入"逐设备加密身份",使每一条测量都能沿着 设备 → 签名测量 → 序列号 → 时间戳 → 标定 → 推理 → 签名事件 的完整链路被追溯与离线复验。读者读完后将理解 ADR-305 解决的威胁模型、签名测量信封的设计细节、单调序列与新鲜度窗口的重放防护机制,以及 ruview-attest 中与验证流水线一一对应的源码级实现,可作为安全部署、二次开发或评审相邻 ADR(296/295/297/318/319)的起点。

ADR-305 在 RuView 感知基座中的定位

RuView 的核心承诺是用商业 WiFi 信号实现室内空间感知与生命体征监测,而推理输出的可信度上限取决于产生它的测量是否可信。ADR-305 是 ADR-300(感知基座项目) 的子文档,拥有其 5 号原语 authenticated sensor identity(认证的传感器身份)。在 ADR-300 的依赖 DAG 中,它与 ADR-306(规范空间本体) 一起作为"脊梁根节点",向下喂养 ADR-301(自动域标定证书)ADR-319(见证链)

在进入设计之前需要先理解一个关键前提:该 ADR 解决的不是"缺少新原语",而是缺少端到端的证据链(chain of custody)。仓库中已经存在相当数量的可复用类型,文档明确要求"复用而非重建":

  • wifi-densepose-rufield 提供 DeviceIdSignatureSignatureBlockFrameProvenanceProvenanceClassSignatureVerifyError 等类型词汇——即"一条被签名的帧"所需的类型语言;
  • wifi-densepose-bfld 提供设备侧 attestation 表面(对应 ADR-141,其 PrivacyAttestationProof 实际定义于 privacy_mode.rs);
  • ADR-295 定义了来源溯源状态机与新鲜度概念(SpatialStateFreshness),单调序列与新鲜度窗口应当嵌入该状态机而非另起炉灶。

此外,该 ADR 给出的实现必须遵守仓库 CLAUDE.md 的两条铁律:"在每个网络、硬件、FFI 边界校验不可信输入,默认最小权限""未在真实硅片上验证前不得声称防伪造能力"——后者在文章后续的验证章节中反复出现。

威胁模型:为什么 IP 白名单不够(ADR-296 的遗留风险)

ADR-305 的直接上游是 ADR-296: Sensor data-plane hardening。ADR-296 解决的问题是:CSI UDP 接收端此前无条件绑定 0.0.0.0:{udp_port}(对应 HTTP 侧 --bind-addr 默认 127.0.0.1 的行为缺失),任何可达该 UDP 端口的宿主机都能注入一个"形状合法"的帧,将自动检测中的服务器切换到实时数据源状态,进而影响存在性/生命体征/自动化输出。

作为"第一步",ADR-296 落地了以下机制:

配置项 语义
--udp-bind(环境变量 RUVIEW_UDP_BIND UDP 绑定地址,默认 127.0.0.1;绑定到可路由地址成为显式操作者选择,桌面/家电默认仍保持 loopback
--udp-allow 可选源 IP/CIDR 白名单;未命中来源的帧被丢弃并计数,loopback 始终放行
启动安全日志 记录绑定范围与白名单是否生效;拒绝"无可信白名单的可路由绑定",除非显式传入 --udp-insecure-lan 覆盖(对应既有 Docker HTTP 拒绝逻辑)

但 ADR-296 在文档中明确承认并记录了这个缺口:IP 白名单无法阻止局域网(LAN)内的 IP 欺骗。同一子网内的任意主机都可以冒用被允许的源 IP 发送帧。它把"每设备预置密钥、MAC/AEAD、设备标识、单调序列号、新鲜度窗口与重放拒绝"整体推迟到后续 ADR——这个后续就是 ADR-305

三个候选方案的取舍

ADR-305 评估了三条路线并记录了拒绝理由:

  1. 停在 ADR-296(绑定 + IP 白名单):被拒绝。ADR-296 自身已明确指出这在可信 LAN 上不足够,子网内任何主机仍可冒用设备身份。
  2. 仅做 TLS/DTLS 传输层认证:被拒绝。它认证的是信道而非测量本身;无法在存储转发(store-and-forward)后存续,无法把序列号绑定进被签名对象,也无法给下游证据/见证层提供可离线复核的东西。
  3. 逐设备签名密钥 + 签名测量信封 + 单调序列 + 新鲜度窗口,复用 RuField/BFLD 既有类型:被采纳。

决策:贯穿传感服务器的"认证帧信封"

ADR-305 的决策核心是引入一个认证帧信封(authenticated frame envelope),由既有 RuField/BFLD 类型组装而成,并贯穿传感服务器全链路。以下四个小节按决策原文编号逐条展开,并与源码实现对照。

1. 逐设备预置身份(Per-device provisioned identity)

设计要点:

  • 每台射频节点(ESP32-S3/C6 节点或适配器)预置一对密钥:设备持有私钥,服务器持有已登记的公钥并绑定到某个 DeviceId
  • 预置是一个显式、被授权的登记(enrollment)步骤——在操作者登记其公钥之前,设备一律视为不可信;
  • 私钥永不写入日志或提交进仓库(遵循 CLAUDE.md 凭据规则);ESP32 侧遵循 firmware/esp32-csi-node 的密钥处理说明;
  • 登记记录把 DeviceId → 公钥 → 能力(capabilities) 绑定起来(经由 ADR-141 的 attestation 表面),因此设备只能对其被证明有能力感知的现象断言测量;这正是 ADR-318(能力证书) 后续消费的输入。

这一"未登记即不可信"的语义在实现中是显式的一等状态ruview-attest 的登记表是一个以 DeviceId 为键、保存"验证器 + 最后接受序列号"的映射,只有调用 enroll() 之后的设备才可能通过校验(见下文第 3 节)。

2. 签名测量信封(Signed measurement envelope)

线缆上传输的帧从裸测量变为一个签名信封

签名覆盖 {DeviceId, sequence, timestamp, measurement-hash} 的规范化序列化;测量本身(CSI/CIR 载荷)由哈希覆盖,因此篡改可被检出,同时不必把整个载荷二次嵌入信封。

校验复用 RuField 的 Signature/SignatureVerifyError签名验证失败的帧被丢弃并计数——正如 ADR-296 丢弃白名单外来源一样,这是边界上的 Err,绝不会降级为"继续处理的警告"。

这一决策在 ruview-attest/src/lib.rs 中得到了逐字实现(该 crate 的模块文档开头即声明 "This crate implements ADR-305 (authenticated sensor identity), phase 1 of the ADR-300 perception substrate")。核心类型形成一个清晰的洋葱结构:

// 被签名的内容:验证者必须能逐字节重建它才能验签
pub struct MeasurementContent {
    pub device: DeviceId,            // 认证的来源设备
    pub sequence: u64,               // 逐设备严格单调序列号(重放防御)
    pub timestamp: Timestamp,        // 设备声称的采集时间戳
    pub payload_hash: PayloadHash,   // 测量载荷的哈希(篡改检测)
    pub calibration_ref: Option<CalibrationRef>, // 生效中的标定证书引用
}

// 线上传输对象,也是 ADR-319 见证链序列化的单元
pub struct SignedMeasurement {
    pub content: MeasurementContent,
    pub signature: Signature,        // 覆盖 canonical_bytes() 的标签
}

实现细节中有三个值得注意的工程决策:

  • 域分离(domain separation):签名输入前置固定前缀 b"ruview-attest/v1\x00signed-measurement\x00",确保本协议产生的标签永远不会与其他用途的哈希混淆;
  • 长度前缀规范化序列化canonical_bytes() 对设备 ID、载荷哈希、标定引用等变长字段统一采用 u32 小端长度前缀 + 字节内容,且不依赖任何 serde 格式——编码无歧义,任一字段都不可能与其他字段混淆,验证者可以稳定重建签名输入;
  • 固定宽度常量时间比较:验签时使用 constant_time_eq 进行常量时间相等比较,规避时序侧信道。

MeasurementContent 的签名输入由 canonical_bytes() 确定性生成(先域分离前缀,再按固定顺序写入 device 字段、sequence(LE u64)、timestamp(LE i64)、长度前缀化的 payload_hash,最后以 1 字节标记区分有无 calibration_ref)。SignedMeasurement::sign() 负责把签名过程封装为"内容构造 → 载荷哈希 → 签名"三步。

3. 单调序列 + 新鲜度窗口(重放防御)

重放(replay)防御由两个互补机制构成:

  • 单调序列:每台设备维护严格递增的逐设备序列号;服务器按 DeviceId 追踪最后接受的序列号,非递增的序列被当作重放拒绝
  • 新鲜度窗口:以服务器时钟偏差预算为界约束 timestamp 与服务器时钟的差距,过期帧被拒绝。

该设计明确复用 ADR-295SpatialStateFreshness 而非发明一套平行的"陈旧"概念,并与 ADR-297 的陈旧节点处理组合工作。

实现侧最核心的部件是 AttestationVerifier(泛型于 Verifier 特征),其 verify() 按严格顺序执行五道关卡,对应着文档要求的每一条拒绝规则:

1. 设备是否已登记?(UnknownDevice)
2. 签名是否有效?(BadSignature)
3. 呈现的载荷是否与签名的 payload_hash 一致?(Tampered)
4. 序列号是否严格大于该设备上次接受的序列?(Replay)
5. 时间戳是否落在新鲜度窗口内?(FreshnessPolicy::check)

关键正确性设计是逐设备序列状态只在全部通过后才推进entry.last_sequence = Some(content.sequence)),因此"被拒绝的帧永远不会消耗一个序列号"——攻击者无法通过发送垃圾帧把合法设备的序列号推到未来、进而合法拒绝其后续帧。同时,verify() 返回的 VerifiedMeasurement 携带已验证的 device/sequence/timestamp/payload_hash/calibration_ref,作为可信链记录继续向后传递,供标定、推理与见证链消费。

一个必须诚实指出的实现事实(与 ADR 的验证纪律一致):仓库当前随附的参考实现是 Blake3MacSigner——一个 keyed-BLAKE3 MAC(对称 MAC),签名者与验证者共享同一把秘密,因此它能演示端到端证据链逻辑,却不提供不可否认性与公钥信任边界。crate 文档明确标注其为 SYNTHETIC-grade reference:使用它得到的任何防伪造保证都是合成级证据,而非现场设备证据;投入部署级使用前需要换用 Ed25519 非对称签名器。架构上这一替换是廉价的——只要为新签名器实现 Signer/Verifier 两个特征(分别基于私钥签名、基于登记公钥验证),信封、序列、新鲜度与防篡改逻辑完全不变。

4. 把证据链带进最终事件

设计上,验证成功后帧的 FrameProvenance 记录已验证的 DeviceId、序列号与时间戳;标定(ADR-301)与推理为各自的变换附加注释;最终发出的空间事件(ADR-306 本体)携带一条已签名的来源谱系(signed provenance lineage)ProvenanceClass 仍强制执行来自 ADR-282/ADR-279(不变量 6)的约束:实测证据链永远不能被别名化为合成,反之亦然。这条端到端已签名谱系正是 ADR-319 见证链要序列化、ADR-318 能力证书要作为证据指向的底层材料。

在统一帧模型 frame.rs 中,这一不变量已经具象化为类型层面的强约束:

  • ProvenanceClass 只有两个变体 Measured / Synthetic,注释明确"两类永不对齐:没有第三个变体,也没有默认值"(frame.rs);
  • FrameProvenanceclass(实测/合成)、evidence(证据阶梯等级)、device_idfirmwarereceipt_id 打包在一起(frame.rs),其中 receipt_id 把推理结果回链到具体帧;
  • EvidenceLevel 定义了从 L0SimulationL5Production 的六级公开证据阶梯(frame.rs);
  • 权威原生帧 RfFrameV2 内置 provenance 字段,且其构造函数强制一条一致规则:Synthetic ⇒ 恰好 L0SimulationMeasured ⇒ 至少 L1CapturedReplay——合成证据永远无法伪装成实测frame.rs)。

因此 ADR-305 的"签名证据链"不是游离在外的附属物,而是直接落在仓库统一帧模型的不变量体系里。

兼容性策略:默认保持可选(opt-in)

框架级安全特性最容易破坏存量部署,因此 ADR-305 明确规定信封为按部署可选、在登记时协商(negotiated at enrollment)

  • 未登记的桌面单节点部署保持现状——在 ADR-296 的 loopback 默认背后以"未认证"方式继续工作,行为不变;
  • 可路由、多节点或车队(fleet)部署(对应 ADR-316)则必须登记身份;
  • ADR-296 的启动安全日志被扩展:明确打印"帧认证是否已启用",让运维人员一眼看清当前部署的安全姿态。

这种"默认安全、进阶可选、日志可读"的渐进路径,是它与 ADR-296 的"loopback 默认 + --udp-insecure-lan 显式覆盖"哲学一脉相承的体现。

影响与权衡

ADR-305 如实记录了采纳后的代价与边界:

  • 收益:ADR-296 明示的遗留风险——LAN 欺骗与重放——对已登记部署被关闭;被认证的是测量本身而非仅信道,因此保证能够在存储转发进入见证链后仍然存续。
  • 运维责任:登记/密钥管理(预置、轮换、吊销)成为部署阶段的操作责任,文档中给出定义;但其车队级分发由 ADR-316 承担,ADR-305 不越界实现。
  • 性能成本:签名验证在摄取边界带来每帧 CPU 开销,文档承认这是"换取可验证证据链的刻意代价",并要求在验证章节中以基准测量上界。
  • 合约演进:帧合约增加 schema 字段;未登记部署不受影响,迁移访问器效仿 ADR-297 的方式。
  • 诚实的证据边界任何防伪造声明在真实硅片上验证之前都不算被实测——一套通过的单元/集成测试只证明逻辑正确,不证明现场设备路径可用。

验证计划与验收门槛

ADR-305 的验证矩阵(对应文档 "Validation" 章节)本身就是一套清晰的验收标准,与 ruview-attest 的校验关卡一一对应:

层级 覆盖场景
单元测试(cargo test -p wifi-densepose-sensing-server-p wifi-densepose-rufield 合法信封被接受;坏签名被拒绝并计数;非单调序列被当作重放拒绝;超出新鲜度窗口的时间戳被拒绝;未登记 DeviceId 被拒绝;实测/合成来源别名被拒绝(ADR-279 不变量 6)
集成测试 捕获/合成的多帧流产出可验证的 device → … → signed event 谱系,ADR-319 可离线序列化并复验
基准(cargo bench 测量每帧验证成本,为摄取开销设定上界
真实硅片证据(硬性要求) 部署级认证声明前必须提供"已登记 ESP32 节点端到端签名帧"的启动/运行日志;成功的构建或模拟器运行不算硬件证据

值得注意:ADR 正文引用的 crate 名(wifi-densepose-rufield)与当前实现 crate 名(v2/crates/ruview-attest)存在演进差异——ruview-attest 的模块文档自述实现 ADR-305,因此在阅读测试命令时建议同时对照两个包名,以仓库实际目录结构为准。

结语:与相邻 ADR 一起读

ADR-305 是 RuView 感知基座安全脉络上承前启后的关键一环。若要完整理解它的设计,建议沿下列文档线索继续阅读(均以仓库根目录为基准):

对一个把"无摄像头也能感知世界"作为目标的系统而言,ADR-305 回答的是一个根本问题:当系统开始影响存在性判断与生命体征告警时,你如何证明进来的每条测量真的来自你信任的那台设备? 从绑定地址到 IP 白名单,再到逐设备加密身份与端到端签名证据链,这条演进路径本身就是"默认最小权限 + 在边界验证不可信输入"原则在射频数据平面上的完整落地示范。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
595
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
918
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.6 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
517
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
389