mirror of
https://github.com/ruvnet/RuView
synced 2026-08-11 20:41:44 +00:00
148 lines
7.6 KiB
Markdown
148 lines
7.6 KiB
Markdown
# 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.
|