← RFC

Loomwave Protocol Specification — Security Layer

Stage 3 / Checkpoint C deliverable. Status: Draft normative spec. Date: 2026-06-09. Depends on: packet-format.md (AEAD_TAG, SEQ, EPOCH, nonce derivation, SIG fields). Analysis basis: security-options.md (bandwidth budget; ~16 B/packet ≈ 0% over the MAC budget).

Normative language per RFC 2119/8174. This document specifies identities, key agreement, per-frame confidentiality+integrity, broadcast authentication, replay protection without clock sync, and reboot behaviour. It is sized to LoRa's bandwidth: no wire handshake, no transmitted nonce, an 8-byte tag (packet-format.md §11).


1. Threat model and goals

1.1 Authentication, not authorization — the rogue-coordinator boundary

Red-team finding F8. This is the most important limitation to state plainly, because a naive reading of "self-certifying addresses + signed beacons" overclaims.

Loomwave authenticates identities — a key proves it owns its ADDR24, and a signature proves a frame came from that key. Loomwave does not, by default, authorize authority: there is no enrollment authority and no PKI (goal #4), so possessing a valid identity does not prove a node is entitled to any role. In particular, any node can act as a coordinator. An adversary can:

  1. Generate a keypair (free) and advertise a coordinator-claim ADVERT (packet-format.md §8.3) with a plausible election score. Being the first or loudest beacon a new client hears, it is TOFU-trusted (§2) like any other coordinator.
  2. Receive that client's REGISTER and DATA — which the client sealed to the attacker's advertised key — and therefore decrypt them. The attacker harvests positions/identities and grants or denies slots at will (selective DoS or full cell capture).

The election corroboration of routing-spec.md §5 raises the cost of winning election among honest participants (a claim is weighted by other nodes' measured reach, so a node cannot simply assert a high score), but it does not stop a client from TOFU-trusting a rogue it hears first, and corroboration is itself Sybil-gameable (an attacker with K radios manufactures K corroborations — §1.2).

Default posture (v1): Loomwave is an open mesh — like Meshtastic/MeshCore, it trusts the RF environment. In an untrusted-RF setting a rogue coordinator can capture new clients. This is accepted and documented, not silently assumed.

Optional hardening (provisioned networks, future): a deployment that needs authority MAY provision a channel endorsement key out of band (the same out-of-band step the channel PSK already requires, §4). Coordinator claims and LSAs are then only honored if endorsed by that key; clients in a provisioned channel reject unendorsed coordinators. This is default-off so the zero-config open mesh is unaffected, and it also bounds backbone LSA-poisoning (red-team F14). Specified later; flagged here so implementations reserve the endorsement field.

1.2 Sybil and admission

Identities are free (ADDR24 = TRUNC_24(H(pubkey)), generated on-device), so there is no per-identity cost. An attacker can mint many identities to exhaust a cell's slot table (admission DoS) or to manufacture election corroboration (§1.1). v1 does not attempt a global cost function (proof-of-work is battery-hostile and community-corrosive — explicitly WONTFIX as a default). The partial defense the MAC SHOULD adopt is airtime-based admission: meter slot grants against airtime heard from a source's radio, not per identity — because Sybil identities from one attacker share one physical radio, so an airtime budget blunts the cheapest attack without penalizing honest nodes. The residual (a true multi-radio Sybil costs the attacker hardware) is accepted.

1.3 Metadata and traffic analysis (accepted for v1)

Red-team finding F19. Restates and sharpens the §1 "out of scope" line.

Per-frame AEAD hides payloads; the structure of a scheduled network leaks more than a flood does:

A flood has a crowd's anonymity; an order is a surveillance affordance. For the v1 threat model (hobbyist/community mesh) this is accepted and documented. Privacy-sensitive deployments must treat it as disqualifying. A mitigation exists (slotmap by ephemeral slot-id rotated per epoch rather than ADDR24, which complicates grant/ACK addressing) and is tracked as research, not committed.

2. Identities

3. Key agreement — implicit ECDH, no wire handshake

For a unicast pair (A,B), the shared secret is static-static X25519:

K_AB = HKDF( X25519(sk_A, pk_B) , info = "loomwave/v1/pair" )

Both sides compute K_AB from cached public keys — nothing is transmitted to establish it (the airtime win over any TLS/Noise handshake). Per-epoch and per-direction subkeys are derived (§5) so the static secret is never used directly as an AEAD key.

Trade-off (stated honestly). Static-static ECDH gives no forward secrecy: compromise of a long-term key exposes past traffic to that peer. The alternative — an ephemeral handshake — costs multiple round-trips of airtime per session, which security-options.md showed is unaffordable at LoRa rates for a messaging/telemetry system. v1 accepts the trade; epoch rotation (§5) limits the exposure window and a future version MAY add an OPTIONAL ephemeral ratchet for high-value channels (a VER-gated extension).

4. Broadcast / group (channel) keys

5. Per-frame AEAD

5.1 Nonce-uniqueness invariant (the SEQ-wrap / EPOCH-rotation rule)

The security of AEAD requires (key, nonce) never repeats. SEQ is 1 byte (wraps at 256) and EPOCH is part of the nonce and the subkey, so the invariant is maintained by this normative rule:

This makes 256-frame SEQ safe: the (key,nonce) space is really (EPOCH, SEQ, DIR), and EPOCH monotonicity guarantees no repeat. The 1-byte SEQ keeps DATA overhead at 16 B while epoch rotation carries the uniqueness burden.

6. Broadcast authentication — truncated Ed25519

7. Replay protection — sliding window, no clock

Replay is prevented without any clock using (EPOCH, SEQ) and a per-peer sliding window:

7.1 Volatile peer caches and re-advertisement

A coordinator's TOFU identity cache MAY be volatile; a restarted coordinator silently drops sealed frames from peers it no longer knows. Clients therefore MUST re-transmit their identity ADVERT periodically while unregistered (suggested: after every 4 unanswered REGISTERs) — observed on hardware as a permanent join-failure loop without it.

8. Reboot and persistence

A naive reboot would reuse low SEQ/EPOCH values and risk nonce reuse. Normative handling:

9. Bandwidth budget (frozen)

Item Wire cost When
AEAD tag 8 B every sealed frame (DATA/REGISTER)
Nonce 0 B derived (§5)
Key-agreement handshake 0 B implicit static ECDH (§3)
Signature 64 B (full Ed25519; see §6 erratum) only on rate-capped BEACON/ADVERT
Epoch (in beacon) 4 B once per superframe, broadcast

DATA security overhead = 8 B within the 16 B header+tag total → 238 B app payload. This meets the security-options.md budget with no payload regression.

10. Conformance (security)

A conformant node MUST: (1) use self-certifying identities and verify advertised keys hash to their address (§2); (2) derive pair/channel keys with no wire handshake (§3–4); (3) AEAD-seal DATA with an 8-byte tag, header as AAD, derived nonce (§5); (4) never reuse (key,nonce) — honor the SEQ-wrap/EPOCH-rotation rule (§5.1); (5) verify full Ed25519 signatures on beacons/adverts (§6); (6) enforce the sliding-window replay check without clocks (§7); (7) advance EPOCH across reboot before transmitting (§8). A node MUST discard any frame failing integrity/signature/replay before relaying or surfacing it (packet-format.md §9).


Builds on packet-format.md; consumed by mac-spec.md (beacon signing, epoch keying) and routing-spec.md (self-certifying addresses, signed LSAs/claims). Reviewed at Checkpoint C before firmware.