SophaxChat · Code Richard Metzner

02 · In words, before code

wiki/Protocol.md

118 lines · Markdown

# Protocol

Full specification: [`docs/protocol.md`](https://github.com/richardmetzner/SophaxChat/blob/main/docs/protocol.md)

---

## Wire format

All messages are JSON-encoded, signed with the sender's Ed25519 identity key, and wrapped in a `WireMessage` envelope before being handed to the transport layer (BLE/WiFi mesh or TCP/Tor).

```
WireMessage {
  type:       WireMessageType   // see enum below
  senderID:   String            // SHA-256(IK_pub) — derived, never a username
  recipientID: String?          // nil for broadcasts
  payload:    Data              // encrypted content (type-dependent)
  signature:  Data              // Ed25519(senderIdentityKey, payload)
  timestamp:  UInt64            // sender wall clock (ms) — used for display only
  messageID:  String            // UUID for dedup
  ttl:        UInt8             // relay hop counter, max 6
}
```

---

## 1:1 Session lifecycle

```
Alice                                       Bob
  │                                          │
  │── Hello (PreKeyBundle) ────────────────▶ │  IK_A, SPK_A, OTPK_A, avatar
  │                                          │  Bob runs X3DH receiver:
  │                                          │  SK = KDF(DH₁ ‖ DH₂ ‖ DH₃ ‖ DH₄)
  │                                          │  Init Double Ratchet with SK
  │◀─ Hello (PreKeyBundle) ──────────────── │  (symmetric)
  │                                          │
  │── InitiateSession ────────────────────▶ │  Alice's ephemeral key + encrypted first msg
  │   (X3DH sender, DR init)                │  Bob decrypts, session established
  │                                          │
  │══ Message (Double Ratchet) ════════════ │  Per-message keys, forward secrecy
  │◀═ Message (Double Ratchet) ════════════ │
```

### X3DH key material

| Key | Type | Purpose |
|---|---|---|
| IK (Identity Key) | X25519 | Long-term identity — never changes |
| SPK (Signed Prekey) | X25519 | Medium-term — rotated every 7 days |
| OTPK (One-Time Prekey) | X25519 | Single-use — consumed on session init |
| EK (Ephemeral Key) | X25519 | Per-session — generated by sender |

Shared secret: `SK = HKDF(DH(IK_A, IK_B) ‖ DH(EK_A, IK_B) ‖ DH(EK_A, SPK_B) ‖ DH(EK_A, OTPK_B))`

---

## Group session lifecycle

SophaxChat supports two group encryption modes:

### Sender Keys v2 (default)

Each member maintains an independent HMAC-SHA256 ratchet chain. No shared group key exists.

```
Alice joins group G
  ├─ Generates SenderKeyState: chainKey, messageCount, createdAt
  ├─ Distributes SenderKeyDistribution to all members
  │
Per message:
  ├─ messageKey  = HMAC-SHA256(chainKey, 0x01)
  ├─ chainKey    = HMAC-SHA256(chainKey, 0x02)   ← ratchet forward
  └─ ChaChaPoly(messageKey, plaintext) → ciphertext
```

Auto-rotation triggers: **500 messages** or **7 days**, whichever comes first.

### MLS — RFC 9420 (opt-in)

MLS groups use `mls-rs` (Rust, compiled to XCFramework via UniFFI) with the RustCrypto backend. Each epoch provides post-compromise security. Migration from SKv2 → MLS is available via `GroupCryptoMigration`.

---

## Relay routing

Messages not directly deliverable are wrapped in a `RelayEnvelope` and broadcast:

```
RelayEnvelope {
  innerMessage: Data   // sealed — relay cannot read content or identity
  ttl:          UInt8  // starts at 6, decremented each hop
  messageID:    String // LRU dedup cache (capacity 500) prevents loops
}
```

A node relays if `ttl > 0` and `messageID` is not in its dedup cache. Maximum path: 6 hops (~300m Bluetooth radius per hop).

---

## Message types

| Type | Description |
|---|---|
| `hello` | PreKeyBundle broadcast on peer connect |
| `initiateSession` | X3DH + first DR message |
| `message` | Normal Double Ratchet message |
| `ack` | Delivery acknowledgement |
| `readReceipt` | Read receipt (blue tick) |
| `typingIndicator` | Ephemeral typing state |
| `groupInvite` | Group membership invitation |
| `groupMessage` | Encrypted group message |
| `senderKeyDistribution` | SKv2 chain distribution / rotation |
| `senderKeyRequest` | Request fresh SKD after stale key detected |
| `reaction` | Emoji reaction on a message |
| `editMessage` | Edit a previously sent message |
| `deleteMessage` | Delete a message |
| `relay` | Multihop relay envelope |
| `deadDrop` | Unauthenticated broadcast (offline delivery) |