Files
ruvnet--RuView/api-docs/adr/ADR-305-authenticated-sensor-identity.md
T
2026-08-11 17:41:30 +00:00

7.6 KiB

ADR-305: Authenticated sensor identity — RF chain of custody

  • Status: Accepted — initial implementation planned (ADR-300 phase 1)
  • Date: 2026-08-11
  • Deciders: ruv
  • Tags: security, identity, provenance, sensor-ingest, attestation, phase-1

Context

This ADR is a child of ADR-300 (perception substrate program) and owns primitive #5, authenticated sensor identity. In the ADR-300 dependency DAG it is a spine root that, together with ADR-306 (canonical spatial ontology), feeds ADR-301 (calibration certificate) and ADR-319 (witness chain).

RuView's inference outputs are only as trustworthy as the measurements that produced them, yet today a measurement's origin is essentially assertional. The UDP data plane accepts frames from any reachable host: ADR-296 shipped step one — a loopback-default bind (--udp-bind) and an optional source IP/CIDR allowlist — and explicitly deferred to a follow-up ADR "per-device provisioned keys, MAC/AEAD, device identifiers, monotonic sequence numbers, freshness window, and replay rejection." This ADR is that step two. ADR-296 correctly documented that an IP allowlist does not stop LAN spoofing; a cryptographic device identity is what closes that gap.

Foundations already exist in the tree and must be reused rather than rebuilt:

  • wifi-densepose-rufield provides DeviceId, Signature, SignatureBlock, FrameProvenance, ProvenanceClass, and SignatureVerifyError — the type vocabulary for a signed frame.
  • wifi-densepose-bfld provides CapabilityAttestation and PrivacyAttestationProof (BFLD attestation, ADR-141) — the device-side attestation surface.
  • ADR-295 defines the source-provenance state machine and freshness (SpatialStateFreshness); a monotonic sequence and freshness window slot into that machine rather than duplicating it.

The gap is not new primitives but an end-to-end chain of custody: a frame must be traceable as device → signed measurement → sequence → timestamp → calibration → inference → signed event, with every link verified at the ingest boundary per CLAUDE.md ("validate untrusted input at every network, hardware, and FFI boundary; default to least authority").

Options considered

  1. Stop at ADR-296 (bind + IP allowlist). Rejected: ADR-296 itself names this insufficient on a trusted LAN; any on-subnet host can still spoof a device.
  2. TLS/DTLS transport authentication only. Rejected: authenticates the channel, not the measurement. It does not survive store-and-forward, does not bind a sequence number into the signed object, and gives the downstream evidence/witness layers nothing to re-verify offline.
  3. Per-device signing keys with a signed measurement envelope, monotonic sequence, and freshness window, reusing the RuField/BFLD types. Chosen.

Decision

Introduce an authenticated frame envelope carried through the sensing server, built from existing RuField/BFLD types.

1. Per-device provisioned identity

  • Each radio (ESP32-S3/C6 node or adapter) is provisioned with a keypair; the device holds the private key, the server holds the enrolled public key bound to a DeviceId. Provisioning is an explicit, authorized enrollment step — a device is untrusted until an operator enrolls its public key. Private keys are never logged or committed (CLAUDE.md credential rule); the ESP32 side follows firmware/esp32-csi-node key-handling notes.
  • The enrollment record binds DeviceId → public key → capabilities (via CapabilityAttestation, ADR-141), so a device can only assert measurements for phenomena it is attested to sense. This is what ADR-318 (capability certificate) later consumes.

2. Signed measurement envelope

  • A frame on the wire becomes a SignatureBlock over the canonical serialization of {DeviceId, sequence, timestamp, measurement-hash}. The measurement itself (CSI/CIR payload) is covered by the hash so tampering is detectable without embedding the whole payload twice.
  • Verification uses Signature/SignatureVerifyError from wifi-densepose-rufield. A frame that fails signature verification is dropped and counted, exactly as ADR-296 drops disallowed sources — an Err at the boundary, never a warning that proceeds.

3. Monotonic sequence + freshness (replay defense)

  • Each device maintains a strictly monotonic per-device sequence number. The server tracks the last accepted sequence per DeviceId; a non-increasing sequence is rejected as a replay.
  • A freshness window bounds timestamp against the server clock skew budget; stale frames are rejected. This reuses ADR-295's SpatialStateFreshness rather than inventing a parallel notion of staleness, and composes with ADR-297's stale-node handling.

4. Chain of custody into the event

  • On successful verification the frame's FrameProvenance records the verified DeviceId, sequence, and timestamp. Calibration (ADR-301) and inference annotate their transforms, and the emitted spatial event (ADR-306 ontology) carries a signed provenance lineage. ProvenanceClass still enforces the synthetic/measured invariant from ADR-282/ADR-279 (invariant 6): a measured chain of custody can never be aliased to synthetic and vice-versa.
  • This end-to-end signed lineage is the substrate the ADR-319 witness chain serializes and the ADR-318 capability certificate points at as evidence.

Compatibility

  • The envelope is opt-in per deployment and negotiated at enrollment. An un-enrolled single-node desktop deployment keeps working unauthenticated behind ADR-296's loopback default; a routable, multi-node, or fleet deployment (ADR-316) requires enrolled identities. The startup security log (ADR-296) is extended to state whether frame authentication is active.

Consequences

  • LAN spoofing and replay — the residual risks ADR-296 named plainly — are closed for enrolled deployments. The measurement, not merely the channel, is authenticated, so the guarantee survives store-and-forward into the witness chain.
  • Enrollment/key-management is now an operational responsibility (provisioning, rotation, revocation). This is documented as a deployment step; key rotation and revocation lists are specified here but their fleet distribution is owned by ADR-316.
  • Signature verification adds per-frame CPU cost at ingest; bounded and measured in validation below. It is a deliberate cost for a verifiable chain of custody.
  • A schema addition to the frame contract; un-enrolled deployments are unaffected, and the migration accessor mirrors ADR-297's approach.
  • No spoof-resistance claim is MEASURED until validated on real silicon (CLAUDE.md hardware rule): a passing unit/integration suite demonstrates the logic, not the fielded device path.

Validation

  • Unit tests (cargo test -p wifi-densepose-sensing-server, -p wifi-densepose-rufield): valid envelope accepted; bad signature rejected and counted; non-monotonic sequence rejected as replay; out-of- window timestamp rejected; un-enrolled DeviceId rejected; measured/synthetic provenance aliasing rejected (ADR-279 invariant 6).
  • Integration test: a captured/synthesized multi-frame stream produces a verifiable device → … → signed event lineage that ADR-319 can serialize and re-verify offline.
  • Benchmark (cargo bench): per-frame verification cost, to bound ingest overhead.
  • Real-silicon evidence required before any deployment-grade authentication claim: a captured boot/runtime log from an enrolled ESP32 node signing frames end-to-end. A successful build or simulator run is not hardware evidence.