docs: add mesh protocol gap analysis and MLS-Lite design

Honest assessment of QuicProChat vs Reticulum/Meshtastic/Briar:
- MLS overhead (500-800 byte KeyPackages) impractical for SF12 LoRa
- KeyPackage distribution over mesh unsolved
- No lightweight mode for constrained links

MLS-Lite design proposes 41-byte overhead symmetric mode:
- ChaCha20-Poly1305 with HKDF key derivation
- Optional Ed25519 signatures
- Upgrade path to full MLS when faster transport available
- QR code / out-of-band key exchange
This commit is contained in:
2026-03-30 23:29:44 +02:00
parent f9ac921a0c
commit 01bc2a4273
2 changed files with 648 additions and 0 deletions

View File

@@ -0,0 +1,325 @@
# MLS-Lite: Lightweight Crypto for Constrained Mesh Links
> **Goal:** Define a symmetric encryption mode that works on LoRa SF12 (51-byte MTU)
> while preserving as much MLS security as possible and enabling upgrade to full MLS
> when faster transports are available.
>
> Created: 2026-03-30 | Status: Design Draft
---
## Problem Statement
Full MLS is impractical on constrained links:
| MLS Operation | Size (bytes) | SF12 Fragments | TX Time (1% duty) |
|---------------|--------------|----------------|-------------------|
| KeyPackage | 500-800 | 10-16 | 10-16 hours |
| Welcome | 1000-2000 | 20-40 | 20-40 hours |
| Commit | 200-500 | 4-10 | 4-10 hours |
| AppMessage | 100-200 | 2-4 | 2-4 hours |
**Result:** Group setup over LoRa takes days. Messages take hours. Unusable.
---
## Design Goals
1. **Short message overhead:** <50 bytes for a "hello" message (fits SF12 MTU unfragmented)
2. **Group encryption:** Shared symmetric key, not just link encryption
3. **Sender authentication:** Ed25519 signature (64 bytes, fragmentable)
4. **Upgrade path:** Seamless transition to full MLS when faster link available
5. **No KeyPackage exchange:** Use pre-shared secrets or out-of-band key exchange
---
## MLS-Lite Protocol
### Mode Selection
```
┌─────────────────────────────────────────────────────────────┐
│ TransportManager │
├─────────────────────────────────────────────────────────────┤
│ On send(destination, payload): │
│ │
│ 1. Check best route to destination │
│ 2. Get transport bitrate: │
│ - QUIC/TCP (>10 kbps) → full MLS │
│ - LoRa SF7-9 (1-10 kbps) → MLS-Lite + signatures │
│ - LoRa SF10-12 (<1 kbps) → MLS-Lite, no signatures │
│ │
│ 3. Wrap payload in appropriate envelope │
│ 4. Fragment if needed for transport MTU │
│ │
└─────────────────────────────────────────────────────────────┘
```
### MLS-Lite Envelope (Minimal Mode)
For SF12 LoRa where every byte counts:
```rust
pub struct MlsLiteEnvelope {
// Header: 25 bytes
pub version: u8, // 1 byte: 0x02 = MLS-Lite
pub flags: u8, // 1 byte: [has_sig, priority(2), reserved(5)]
pub group_id: [u8; 8], // 8 bytes: truncated group identifier
pub sender_addr: [u8; 4], // 4 bytes: truncated sender address
pub seq: u32, // 4 bytes: sequence number (replay protection)
pub epoch: u16, // 2 bytes: key epoch (for rotation)
pub nonce: [u8; 5], // 5 bytes: ChaCha20 nonce suffix (epoch is prefix)
// Payload: variable
pub ciphertext: Vec<u8>, // ChaCha20-Poly1305 encrypted
// includes 16-byte auth tag
// Optional signature: 64 bytes (if has_sig flag set)
pub signature: Option<[u8; 64]>,
}
// Minimal overhead: 25 bytes header + 16 bytes tag = 41 bytes
// With signature: 105 bytes total overhead
```
### Encryption Details
```
Key derivation:
group_secret = HKDF-SHA256(
ikm = pre_shared_key || group_id,
salt = "quicprochat-mls-lite-v1",
info = epoch.to_be_bytes()
)
encryption_key = group_secret[0..32] // ChaCha20 key
nonce_prefix = group_secret[32..39] // 7 bytes
Full nonce (12 bytes):
nonce = nonce_prefix || envelope.nonce
Encrypt:
ciphertext = ChaCha20-Poly1305(
key = encryption_key,
nonce = nonce,
plaintext = payload,
aad = header_bytes // version, flags, group_id, sender_addr, seq, epoch
)
```
### Key Exchange (Out-of-Band)
MLS-Lite groups are established via:
1. **QR Code:** Scan to join group (contains group_secret + group_id)
2. **NFC Tap:** Bump phones to exchange group key
3. **Voice Readout:** 24-word mnemonic for group secret
4. **Faster Link:** Full MLS setup over QUIC, then extract epoch key for MLS-Lite
```
┌─────────────────────────────────────────────────────────────┐
│ Key Exchange Flow │
├─────────────────────────────────────────────────────────────┤
│ │
│ Option A: QR Code (in-person) │
│ Alice generates: QR(group_id || group_secret) │
│ Bob scans → joins MLS-Lite group │
│ │
│ Option B: MLS Bootstrap (hybrid) │
│ 1. Alice & Bob establish full MLS group over Internet │
│ 2. Export current epoch key as MLS-Lite group_secret │
│ 3. Both can now communicate over LoRa using MLS-Lite │
│ 4. When Internet available, re-sync to full MLS │
│ │
│ Option C: Pre-Shared Key (deployment) │
│ Org distributes group_secret to all devices │
│ Like Meshtastic channel key, but with replay protection │
│ │
└─────────────────────────────────────────────────────────────┘
```
### Key Rotation
MLS-Lite does NOT have automatic post-compromise security. Manual rotation:
```
Rotation trigger:
- Periodic (e.g., weekly)
- Member leaves group
- Suspected compromise
Rotation process:
1. New group_secret generated (QR code, or via full MLS if available)
2. epoch incremented
3. Old key deleted after grace period
4. Devices that miss rotation must re-join
```
### Upgrade to Full MLS
When faster transport becomes available:
```
┌─────────────────────────────────────────────────────────────┐
│ MLS-Lite → MLS Upgrade │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. Device detects QUIC/TCP connectivity │
│ 2. Contacts server, fetches peer KeyPackages │
│ 3. Creates full MLS group with same group_id │
│ 4. Sends MLS Welcome to all known members │
│ 5. Members upgrade to full MLS │
│ 6. MLS-Lite continues in parallel for LoRa-only members │
│ │
│ Bridging: │
│ - Gateway nodes (CAP_GATEWAY) translate between modes │
│ - Full MLS message → re-encrypt as MLS-Lite for LoRa │
│ - MLS-Lite message → forward as MLS AppMessage │
│ │
└─────────────────────────────────────────────────────────────┘
```
---
## Security Analysis
### What MLS-Lite Provides
| Property | Full MLS | MLS-Lite | Notes |
|----------|----------|----------|-------|
| **Confidentiality** | ✓ | ✓ | ChaCha20-Poly1305 |
| **Integrity** | ✓ | ✓ | Poly1305 MAC |
| **Replay protection** | ✓ | ✓ | Sequence numbers |
| **Sender auth (group)** | ✓ | ✓ | Only group members can encrypt |
| **Sender auth (individual)** | ✓ | Optional | Ed25519 signature (64 bytes) |
| **Forward secrecy** | ✓ | Partial | Only on manual epoch rotation |
| **Post-compromise security** | ✓ | ✗ | No automatic healing |
| **Transcript consistency** | ✓ | ✗ | No ratchet tree |
| **Deniability** | ✗ | ✗ | Neither provides this |
### Threat Model
**Protected against:**
- Passive eavesdropping (even quantum with PQ group_secret)
- Message replay (sequence numbers)
- Message tampering (AEAD)
- Outsider injection (need group_secret)
**NOT protected against:**
- Compromised group member reading all traffic (no PCS)
- Long-term key compromise without manual rotation
- Relay node with group_secret (but they're in the group anyway)
### Comparison to Meshtastic
| Property | Meshtastic | MLS-Lite |
|----------|------------|----------|
| **Encryption** | AES-256-CTR | ChaCha20-Poly1305 |
| **Authentication** | None (shared key) | Optional Ed25519 |
| **Replay protection** | None | Sequence numbers |
| **Key rotation** | Manual | Manual (epoch field) |
| **Overhead** | 16 bytes (header) | 41 bytes (no sig), 105 bytes (with sig) |
| **Upgrade path** | None | → Full MLS |
MLS-Lite is strictly better than Meshtastic's crypto while fitting similar constraints.
---
## Wire Format
### MLS-Lite Envelope (CBOR)
```
MlsLiteEnvelope = {
0: uint, ; version (0x02)
1: uint, ; flags
2: bytes .size 8, ; group_id
3: bytes .size 4, ; sender_addr
4: uint, ; seq
5: uint, ; epoch
6: bytes .size 5, ; nonce
7: bytes, ; ciphertext (includes 16-byte tag)
? 8: bytes .size 64 ; signature (optional)
}
```
Estimated sizes:
- Minimal (1-byte payload): ~50 bytes (fits SF12 unfragmented!)
- Short message (20 bytes): ~70 bytes (2 fragments on SF12)
- With signature: add 64 bytes
### MeshEnvelope Mode Flag
Extend MeshEnvelope to indicate crypto mode:
```rust
pub struct MeshEnvelope {
// ... existing fields ...
/// Crypto mode: 0x00 = full MLS, 0x02 = MLS-Lite
pub crypto_mode: u8,
}
```
---
## Implementation Plan
### Phase 1: Core MLS-Lite
1. [ ] Define `MlsLiteEnvelope` struct
2. [ ] Implement key derivation (HKDF)
3. [ ] Implement encrypt/decrypt (ChaCha20-Poly1305)
4. [ ] Add sequence number tracking (replay window)
5. [ ] Add CBOR serialization
6. [ ] Unit tests
### Phase 2: Integration
1. [ ] Add `crypto_mode` to TransportManager routing decisions
2. [ ] Implement QR code key exchange (generate/scan)
3. [ ] Add `/mesh lite-create <name>` REPL command
4. [ ] Add `/mesh lite-join <qr-data>` REPL command
5. [ ] Integration tests with LoRaMockMedium
### Phase 3: Gateway/Bridge
1. [ ] Implement MLS → MLS-Lite translation in gateway nodes
2. [ ] Add CAP_GATEWAY capability flag
3. [ ] Handle epoch sync between modes
4. [ ] End-to-end test: QUIC client → gateway → LoRa client
---
## Open Questions
1. **Signature vs. no signature?**
- Signatures add 64 bytes (1-2 extra fragments on SF12)
- Without signatures, any group member can spoof any sender
- Proposal: configurable, default to signatures on SF7-9, skip on SF10-12
2. **Epoch sync without server?**
- How do LoRa-only nodes learn about epoch changes?
- Proposal: Include epoch in announce, peers relay epoch updates
3. **Post-quantum group_secret?**
- MLS-Lite uses symmetric crypto (quantum-safe for confidentiality)
- Key exchange is vulnerable if using X25519
- Proposal: QR code includes ML-KEM-768 encapsulation for PQ key exchange
4. **Compatibility with Reticulum/LXMF?**
- Should we use msgpack instead of CBOR for LXMF compat?
- Should we implement LXMF as an additional mode?
---
## References
- [MLS RFC 9420](https://datatracker.ietf.org/doc/rfc9420/) — Full MLS spec
- [ChaCha20-Poly1305 RFC 8439](https://datatracker.ietf.org/doc/rfc8439/)
- [HKDF RFC 5869](https://datatracker.ietf.org/doc/rfc5869/)
- [Meshtastic Encryption](https://meshtastic.org/docs/overview/encryption/)
- [Reticulum LXMF](https://github.com/markqvist/LXMF)
---
*Last updated: 2026-03-30*