transport-security.md
1 # Ekko Transport Security Documentation 2 3 **Version**: 1.0 4 **Date**: March 2026 5 **Classification**: Technical Reference 6 7 --- 8 9 ## Table of Contents 10 11 1. [Overview](#1-overview) 12 2. [Core Cryptographic Primitives](#2-core-cryptographic-primitives) 13 3. [Message Encryption Pipeline](#3-message-encryption-pipeline) 14 4. [BLE (Bluetooth Low Energy)](#4-ble-bluetooth-low-energy) 15 5. [DHT (Distributed Hash Table)](#5-dht-distributed-hash-table) 16 6. [Tor P2P (Onion-Routed Peer-to-Peer)](#6-tor-p2p-onion-routed-peer-to-peer) 17 7. [iroh (QUIC-Based P2P)](#7-iroh-quic-based-p2p) 18 8. [BLE Gossip (Proximity Relay)](#8-ble-gossip-proximity-relay) 19 9. [Comparative Security Matrix](#9-comparative-security-matrix) 20 10. [Attack Surface Analysis](#10-attack-surface-analysis) 21 22 --- 23 24 ## 1. Overview 25 26 Ekko is a censorship-resistant messaging application that delivers messages through multiple independent transport channels. All transports share a common application-level encryption layer built on modern cryptographic primitives. Each transport adds its own transport-level security properties. 27 28 **Transport Priority:** 29 - Text messages: BLE → iroh → Tor P2P → DHT 30 - File attachments: BLE → iroh → Tor P2P → DHT (chunked) 31 32 **Key Principle:** Messages are encrypted once at the application layer before being handed to any transport. Transport-level encryption (where present) provides defense-in-depth but is not relied upon for confidentiality. 33 34 --- 35 36 ## 2. Core Cryptographic Primitives 37 38 All cryptographic operations are implemented in Rust using audited crates. No custom cryptography is used. 39 40 ### Key Types 41 42 | Key | Algorithm | Size | Lifetime | Purpose | 43 |-----|-----------|------|----------|---------| 44 | Identity | Ed25519 | 32-byte secret, 32-byte public | Device lifetime | Signing, authentication | 45 | Exchange | X25519 | 32-byte secret, 32-byte public | Device lifetime | Static key agreement | 46 | Ephemeral | X25519 | 32-byte secret, 32-byte public | Single message | Forward secrecy | 47 48 ### Algorithms 49 50 | Function | Algorithm | Parameters | 51 |----------|-----------|------------| 52 | Authenticated encryption | ChaCha20-Poly1305 | 256-bit key, 96-bit nonce, 128-bit tag | 53 | Key agreement | X25519 ECDH | 32-byte shared secret output | 54 | Key derivation | HKDF-SHA256 (RFC 5869) | Domain prefix: `DeadDrop-v1-` | 55 | Signatures | Ed25519 | 64-byte deterministic signatures | 56 | Fingerprints | BLAKE2b-256 | 32 bytes (128 bits displayed to user) | 57 | Memory cleanup | Zeroize | All secret key types implement `ZeroizeOnDrop` | 58 59 ### Crate Dependencies 60 61 | Crate | Version | Purpose | 62 |-------|---------|---------| 63 | `x25519-dalek` | — | X25519 key agreement | 64 | `ed25519-dalek` | — | Ed25519 signatures | 65 | `chacha20poly1305` | — | AEAD encryption | 66 | `hkdf` + `sha2` | — | Key derivation | 67 | `snow` | — | Noise protocol framework | 68 | `rand` | — | OS-level CSPRNG | 69 | `zeroize` | — | Secure memory cleanup | 70 71 --- 72 73 ## 3. Message Encryption Pipeline 74 75 Every message, regardless of transport, passes through the same encryption pipeline. 76 77 ### Encryption (Sender) 78 79 ``` 80 PlaintextMessage {content_type, content, filename, metadata} 81 │ 82 ▼ 83 Generate message_id: 16 random bytes (CSPRNG) 84 │ 85 ▼ 86 Generate ephemeral X25519 key pair 87 │ 88 ▼ 89 ECDH: shared_secret = DH(ephemeral_secret, recipient_exchange_public) 90 │ (32-byte X25519 shared secret) 91 ▼ 92 HKDF-SHA256(secret=shared_secret, salt=message_id, info="DeadDrop-v1-message-key") 93 │ (32-byte encryption key) 94 ▼ 95 ChaCha20-Poly1305.encrypt(key, nonce=random_12_bytes, plaintext, aad=message_id) 96 │ (ciphertext + 16-byte authentication tag) 97 ▼ 98 Ed25519.sign(identity_secret, message_id ∥ ciphertext) 99 │ (64-byte signature) 100 ▼ 101 EncryptedMessage { 102 message_id: [u8; 16] — Random, used as HKDF salt 103 ephemeral_public: [u8; 32] — For recipient ECDH recovery 104 nonce: [u8; 12] — Random, for ChaCha20 105 ciphertext: Vec<u8> — Encrypted content + Poly1305 tag 106 timestamp: i64 — Unix seconds 107 signature: [u8; 64] — Ed25519 over message_id ∥ ciphertext 108 } 109 ``` 110 111 ### Decryption (Recipient) 112 113 ``` 114 EncryptedMessage 115 │ 116 ▼ 117 Ed25519.verify(sender_identity_public, message_id ∥ ciphertext, signature) 118 │ (reject if invalid) 119 ▼ 120 ECDH: shared_secret = DH(recipient_exchange_secret, ephemeral_public) 121 │ (same 32-byte shared secret as sender computed) 122 ▼ 123 HKDF-SHA256(secret=shared_secret, salt=message_id, info="DeadDrop-v1-message-key") 124 │ (same 32-byte encryption key) 125 ▼ 126 ChaCha20-Poly1305.decrypt(key, nonce, ciphertext, aad=message_id) 127 │ (reject if tag verification fails) 128 ▼ 129 PlaintextMessage 130 ``` 131 132 ### Forward Secrecy Guarantee 133 134 Each message generates a fresh ephemeral X25519 key pair. The ephemeral secret is consumed by Rust's ownership system after the DH operation and then zeroized. It is never written to disk or transmitted. 135 136 **Consequence:** If the recipient's long-term exchange key is compromised after messages have been decrypted, past ciphertexts cannot be recovered — the ephemeral secrets no longer exist. 137 138 **Limitation:** There is no Double Ratchet or evolving session state. Each message is encrypted independently. If the recipient's exchange key is compromised *before* they decrypt a message still in transit, that message can be decrypted by the attacker. 139 140 ### Non-Repudiation 141 142 The sender's Ed25519 signature on every message provides non-repudiation — a recipient can cryptographically prove who sent a message. This is a deliberate design choice; deniable authentication is not a goal. 143 144 --- 145 146 ## 4. BLE (Bluetooth Low Energy) 147 148 ### Architecture 149 150 BLE transport provides direct device-to-device messaging with no network infrastructure. Devices discover each other via BLE advertising, establish a Noise-encrypted channel, and exchange queued messages bidirectionally. 151 152 ### Third-Party Services 153 154 **None.** BLE operates entirely peer-to-peer with no internet connectivity required. 155 156 ### Encryption Layers 157 158 | Layer | Protocol | Purpose | 159 |-------|----------|---------| 160 | Transport | Noise_XX_25519_ChaChaPoly_SHA256 | Session encryption, mutual authentication | 161 | Application | ChaCha20-Poly1305 + Ed25519 | Per-message encryption (§3) | 162 163 ### Noise XX Handshake 164 165 The BLE transport uses the Noise Protocol Framework with the XX pattern, providing mutual authentication with encrypted static key transmission. 166 167 ``` 168 Initiator (Alice) Responder (Bob) 169 │ │ 170 │ → e │ Message 1: Alice's ephemeral public key 171 │ (32 bytes, unencrypted) │ 172 │ │ 173 │ ← e, ee, s, es │ Message 2: Bob's ephemeral key + 174 │ (80 bytes, partially encrypted) │ Bob's static key (encrypted with DH(e,e)) 175 │ │ 176 │ → s, se │ Message 3: Alice's static key 177 │ (48 bytes, encrypted) │ (encrypted with established keys) 178 │ │ 179 │ ═══ Symmetric transport keys ═══ │ 180 │ (separate keys per direction) │ 181 ``` 182 183 **After handshake completion:** 184 - Both parties have verified each other's static X25519 keys 185 - Two independent symmetric keys are established (one per direction) 186 - A nonce counter is maintained per direction (never reused) 187 - All subsequent data is encrypted with ChaCha20-Poly1305 188 189 ### Message Exchange Protocol 190 191 After the Noise handshake, an application-level exchange protocol runs over the encrypted channel: 192 193 1. **MsgCount**: Both peers announce how many queued messages they have 194 2. **MsgData**: Each message (already encrypted per §3) is transmitted with index tracking 195 3. **MsgAck/MsgNack**: Per-message acknowledgment/rejection 196 4. **ExchangeComplete**: Both peers signal completion 197 198 ### Forward Secrecy 199 200 BLE provides forward secrecy at two levels: 201 202 1. **Session level** (Noise XX): Each BLE session generates fresh ephemeral keys. Compromise of static keys does not reveal past session keys. 203 2. **Message level** (§3): Each message within a session uses independent ephemeral ECDH, providing per-message forward secrecy even within a single BLE session. 204 205 ### Dual-Initiation Collision 206 207 When both devices simultaneously initiate a BLE connection: 208 - Active sessions in `exchanging`, `gossiping`, or `complete` states ignore incoming handshake messages 209 - If both are at message 1, ephemeral keys are compared — the smaller key's device becomes the responder 210 - Prevents session reset during active data transfer 211 212 ### Security Risks 213 214 | Risk | Severity | Notes | 215 |------|----------|-------| 216 | BLE range limitation | Low | ~10m range limits interception to physical proximity | 217 | BLE advertising metadata | Low | Rotating IDs derived via HKDF prevent tracking; rotated periodically | 218 | Relay attack (MITM) | Low | Noise XX provides mutual authentication; MITM requires real-time key substitution | 219 | Jamming/DoS | Medium | Radio-level jamming can prevent BLE communication; no mitigation possible | 220 221 --- 222 223 ## 5. DHT (Distributed Hash Table) 224 225 ### Architecture 226 227 Messages are encrypted, published to the public BitTorrent Mainline DHT as BEP44 mutable items, and fetched by recipients who poll their designated slots. 228 229 ### Third-Party Services 230 231 | Service | Operator | Role | 232 |---------|----------|------| 233 | Mainline DHT bootstrap nodes | BitTorrent Inc, µTorrent, Transmission, libtorrent, CMU | Initial DHT network entry | 234 | Mainline DHT nodes | Thousands of public nodes | Store and relay BEP44 mutable items | 235 236 **Default bootstrap nodes:** 237 - `router.bittorrent.com:6881` 238 - `router.utorrent.com:6881` 239 - `dht.transmissionbt.com:6881` 240 - `dht.libtorrent.org:25401` 241 - `node.sp.cs.cmu.edu:6881` 242 243 **No Ekko-specific infrastructure is required.** The DHT is the same public network used by BitTorrent clients worldwide. 244 245 ### Encryption Layers 246 247 | Layer | Protocol | Purpose | 248 |-------|----------|---------| 249 | DHT integrity | BEP44 Ed25519 signatures | Prevents unauthorized slot modification | 250 | Application | ChaCha20-Poly1305 + Ed25519 | Per-message encryption (§3) | 251 252 **Note:** The DHT itself provides no confidentiality. BEP44 values are stored in plaintext on DHT nodes. All confidentiality comes from the application-level encryption. 253 254 ### DHT Key Derivation 255 256 Recipients are addressed by their X25519 exchange public key. A BEP44 signing key is derived deterministically: 257 258 ``` 259 ed25519_seed = SHA-512("deaddrop/dht/ed25519/v1" ∥ x25519_exchange_public)[0..32] 260 signing_key = Ed25519.from_seed(ed25519_seed) 261 public_key = signing_key.verifying_key() 262 ``` 263 264 **Slot addressing:** 265 - Salt: `"deaddrop/v1/slot/{N}"` where N = 0..100 266 - Sequence number: current Unix timestamp (prevents rollback) 267 - Value: bencoded list of `DhtStoredMessage` entries, optionally zstd-compressed 268 269 ### Publish/Fetch Flow 270 271 **Publishing (sender):** 272 1. Messages are encrypted per §3 and wrapped in `DhtStoredMessage { message_id, sender_key, payload, timestamp, expires_at }` 273 2. Messages distributed across recipient's slots (20–100 slots, expandable to 500 via continuation chaining) 274 3. Each slot is published as a BEP44 mutable item with Ed25519 signature and current timestamp as sequence number 275 4. Retry: 3 attempts with exponential backoff (1s, 2s, 4s) 276 277 **Fetching (recipient):** 278 1. Recipient polls slots 0–100 concurrently every 30 seconds (first 3 minutes) then every 5 minutes 279 2. Continuation markers chain to additional slot ranges (100–199, 200–299, etc.) 280 3. Messages exceeding 950-byte slot limit are split into chunks with reassembly metadata 281 4. Each fetched message is decrypted per §3 and stored locally 282 283 ### Forward Secrecy 284 285 Per-message forward secrecy is provided by the application-level encryption (§3). Each message uses an independent ephemeral ECDH key agreement. 286 287 **DHT-specific limitation:** Messages persist on DHT nodes for up to 48 hours (configurable TTL). If a recipient's exchange key is compromised during this window, unread messages on the DHT can be decrypted. 288 289 ### Security Risks 290 291 | Risk | Severity | Notes | 292 |------|----------|-------| 293 | Slot enumeration | Medium | Observers can correlate slots 0–100 to a single recipient by access patterns | 294 | Volume analysis | Medium | Number of occupied slots reveals approximate message count | 295 | IP address exposure | Medium | DHT participation reveals the device's IP address to DHT peers | 296 | Timing correlation | Medium | Publish/fetch timing can correlate sender and recipient | 297 | No confidentiality at rest on DHT | Low | Mitigated by application-level encryption; DHT nodes see only ciphertext | 298 | Stale message replay | Low | TTL (48h), message ID deduplication, and BEP44 sequence numbers prevent replay | 299 | Slot value rollback | Low | BEP44 sequence numbers prevent injection of older values | 300 301 --- 302 303 ## 6. Tor P2P (Onion-Routed Peer-to-Peer) 304 305 ### Architecture 306 307 Devices create Tor v3 onion services (.onion hidden services) and establish direct WebSocket connections through the Tor network. Peer discovery uses DHT-based rendezvous — each device publishes its .onion address to the DHT. 308 309 ### Third-Party Services 310 311 | Service | Operator | Role | 312 |---------|----------|------| 313 | Tor network | The Tor Project (volunteer-operated) | Onion routing, circuit building | 314 | Tor directory authorities | The Tor Project | Network consensus | 315 | Mainline DHT | Public | .onion address rendezvous | 316 317 **No exit nodes are used.** All connections are .onion-to-.onion (hidden service to hidden service), remaining entirely within the Tor network. 318 319 ### Encryption Layers 320 321 | Layer | Protocol | Purpose | 322 |-------|----------|---------| 323 | Network | Tor v3 onion routing | Anonymity, 3-hop circuit encryption | 324 | Transport | WebSocket over TCP | Message framing | 325 | Application | ChaCha20-Poly1305 + Ed25519 | Per-message encryption (§3) | 326 327 ### Connection Protocol 328 329 **WebSocket Handshake over Tor:** 330 331 ``` 332 Initiator (Client) Responder (Server) 333 │ │ 334 │ SOCKS5 tunnel via localhost:9050 │ 335 │ ─────────────────────────────────► │ Tor routes to .onion:80 336 │ │ 337 │ WebSocket upgrade (HTTP 101) │ 338 │ ◄────────────────────────────────► │ 339 │ │ 340 │ Send 32-byte Ed25519 identity key │ 341 │ ─────────────────────────────────► │ Server stores peer identity 342 │ │ 343 │ Receive 32-byte identity key │ Server sends own identity 344 │ ◄───────────────────────────────── │ 345 │ │ Server verifies peer is known contact 346 │ Receive 32-byte nonce challenge │ 347 │ ◄───────────────────────────────── │ 348 │ │ 349 │ Echo nonce back │ 350 │ ─────────────────────────────────► │ 351 │ │ 352 │ Receive "OK" │ Authentication complete 353 │ ◄───────────────────────────────── │ 354 │ │ 355 │ ═══ Encrypted messages (§3) ═══ │ 356 │ ◄────────────────────────────────► │ 357 ``` 358 359 ### Rendezvous Mechanism 360 361 Peer .onion addresses are discovered via DHT: 362 363 - **Salt:** `"deaddrop/v1/rendezvous"` 364 - **Value:** bencoded `{ "a": "<56-char-onion-address>", "t": <unix_timestamp> }` 365 - **Freshness:** Entries older than 1 hour are rejected 366 - **Caching:** Discovered addresses cached in `contacts.onion_address` column for instant reconnect 367 368 **Polling:** Fast poll every 30 seconds for first 3 minutes, then every 5 minutes. 369 370 ### Forward Secrecy 371 372 Per-message forward secrecy is provided by the application-level encryption (§3). 373 374 Additionally, Tor provides circuit-level forward secrecy — each Tor circuit uses ephemeral Diffie-Hellman key agreement at each hop. 375 376 ### Platform Implementation 377 378 | Platform | Tor Framework | Hidden Service | SOCKS5 | 379 |----------|--------------|----------------|--------| 380 | iOS | `Tor.framework` (CocoaPods) | `ADD_ONION` via control port | Port 9050 | 381 | macOS | `Tor.framework` (CocoaPods) | `ADD_ONION` via control port | Port 9050 | 382 | Android | `tor_hidden_service` Flutter package | Automatic | Port 9050 | 383 384 **Persistent onion keys:** The Ed25519 private key from `ADD_ONION NEW` is saved to `.onion_key` and reused on subsequent launches for stable .onion addresses. 385 386 ### Security Risks 387 388 | Risk | Severity | Notes | 389 |------|----------|-------| 390 | DHT rendezvous metadata | Medium | DHT queries for .onion addresses are not anonymous; DHT peers see the lookup | 391 | Timing correlation | Medium | Message timing patterns visible to ISP-level observers watching both endpoints | 392 | Message size analysis | Low | No padding; file transfer sizes are observable | 393 | Tor circuit compromise | Low | Requires control of guard + rendezvous point; v3 onion services have improved guard rotation | 394 | Stale .onion address | Low | 1-hour TTL + SOCKS5 status=4 (connection refused) indicates peer offline | 395 | Onion key storage | Low | Stored unencrypted in app sandbox; OS sandboxing provides protection | 396 | Identity key exchange in cleartext over WebSocket | Low | Mitigated by Tor circuit encryption; identity keys are public information | 397 398 --- 399 400 ## 7. iroh (QUIC-Based P2P) 401 402 ### Architecture 403 404 iroh is a QUIC-based P2P library from n0.computer providing NAT traversal via relay servers and optional UDP hole-punching for direct connections. Ekko uses iroh with a custom ALPN protocol (`deaddrop/msg/v1`) for push-based message delivery. 405 406 ### Third-Party Services 407 408 | Service | Operator | Role | 409 |---------|----------|------| 410 | n0.computer relay servers | n0 Inc | QUIC relay for NAT traversal | 411 | Mainline DHT | Public | EndpointId rendezvous | 412 413 **Relay bypass:** If UDP hole-punching succeeds, the relay is not used. The relay is a fallback for devices behind restrictive NATs. 414 415 ### Encryption Layers 416 417 | Layer | Protocol | Purpose | 418 |-------|----------|---------| 419 | Transport | QUIC (TLS 1.3, ChaCha20-Poly1305) | Transport confidentiality via iroh | 420 | Application | ChaCha20-Poly1305 + Ed25519 | Per-message encryption (§3) | 421 422 **Double encryption:** Messages are encrypted at the application layer (§3) before being transmitted over QUIC, which adds its own TLS 1.3 encryption. Even if the QUIC layer is compromised, messages remain protected. 423 424 ### EndpointId and Peer Discovery 425 426 Each device has a persistent 32-byte EndpointId derived from a locally-stored secret key: 427 428 ``` 429 secret_key: 32 random bytes (generated once, stored at {storage_dir}/iroh_secret_key) 430 EndpointId = iroh::Endpoint.id() (derived from secret_key) 431 ``` 432 433 **DHT rendezvous:** 434 - **Salt:** `"deaddrop/v1/iroh-rendezvous"` (distinct from Tor rendezvous) 435 - **Value:** `"endpoint_id|relay_url"` (pipe-separated) 436 - **Caching:** `contacts.iroh_endpoint_id` column + in-memory `IROH_RELAY_CACHE` 437 438 ### Message Flow 439 440 **Sending:** 441 ``` 442 1. Encrypt message per §3 → EncryptedMessage 443 2. bincode::serialize(EncryptedMessage) → payload bytes 444 3. Look up recipient's EndpointId + relay URL 445 4. Open unidirectional QUIC stream: endpoint.connect(addr, "deaddrop/msg/v1") 446 5. Write: [4-byte big-endian length] + [payload] 447 6. Close stream 448 ``` 449 450 **Receiving:** 451 ``` 452 1. endpoint.accept() → incoming QUIC connection 453 2. Read 4-byte length, then payload 454 3. Map sender EndpointId → ContactId via cached mapping 455 4. Look up sender's Ed25519 identity key 456 5. Decrypt per §3 457 6. Store in database 458 ``` 459 460 ### Forward Secrecy 461 462 Per-message forward secrecy is provided by the application-level encryption (§3). 463 464 QUIC/TLS 1.3 provides additional transport-level forward secrecy with ephemeral ECDH per connection. 465 466 ### What the Relay Server Can See 467 468 | Data | Visible to Relay? | 469 |------|-------------------| 470 | Message content | No (double-encrypted) | 471 | Message metadata (timestamps, sizes) | Yes | 472 | Connection patterns (who contacts whom) | Yes (EndpointIds) | 473 | IP addresses of both peers | Yes | 474 | Message frequency | Yes | 475 476 ### Security Risks 477 478 | Risk | Severity | Notes | 479 |------|----------|-------| 480 | Relay metadata visibility | Medium | n0 relay sees connection patterns and IP addresses | 481 | IP address exposure | Medium | Both peers' IPs visible to relay; no anonymity | 482 | DHT endpoint visibility | Low | Published EndpointIds are publicly queryable | 483 | Message size analysis | Low | Packet sizes visible to relay and network observers | 484 | Timing analysis | Low | Message frequency visible; can enable traffic analysis | 485 | Endpoint impersonation | Low | Ed25519 signature verification prevents message forgery | 486 | Nonce reuse | Very Low | Random 96-bit nonces; collision probability negligible | 487 488 --- 489 490 ## 8. BLE Gossip (Proximity Relay) 491 492 ### Architecture 493 494 BLE Gossip extends the BLE transport to relay messages between devices that don't share contacts. After a standard BLE exchange completes, devices enter a gossip phase where they exchange forwarded messages for third-party recipients using the existing Noise-encrypted channel. 495 496 ### Third-Party Services 497 498 **None.** Operates entirely over BLE with no network connectivity. 499 500 ### Encryption 501 502 Gossip messages maintain their original application-level encryption (§3). The gossip relay cannot read forwarded messages because they are encrypted for a specific recipient's exchange key. 503 504 **Transport encryption:** The gossip protocol reuses the Noise transport established during the initial BLE handshake, so forwarded messages are double-encrypted (Noise session + application-level). 505 506 ### Security Properties 507 508 - **Forwarding store limits:** 50MB maximum, 48-hour TTL 509 - **Probabilistic acceptance:** Messages accepted based on bloom filter digests 510 - **No metadata leakage to relay:** Relay device sees encrypted blobs; cannot determine sender, recipient, or content 511 - **Chunking:** Gossip digests are chunked with 4-byte length prefix + 500-byte data chunks for BLE ATT size limits 512 513 --- 514 515 ## 9. Comparative Security Matrix 516 517 | Property | BLE | DHT | Tor P2P | iroh | 518 |----------|-----|-----|---------|------| 519 | **Confidentiality** | ChaCha20-Poly1305 (2 layers) | ChaCha20-Poly1305 | ChaCha20-Poly1305 + Tor circuits | ChaCha20-Poly1305 (2 layers) | 520 | **Authenticity** | Ed25519 + Noise XX | Ed25519 + BEP44 signatures | Ed25519 + identity key exchange | Ed25519 + QUIC TLS | 521 | **Forward secrecy** | Per-session (Noise) + per-message (ECDH) | Per-message (ECDH) | Per-message (ECDH) + per-circuit (Tor) | Per-message (ECDH) + per-connection (TLS 1.3) | 522 | **Anonymity** | Physical proximity required | IP visible to DHT peers | Strong (.onion-to-.onion) | IP visible to relay | 523 | **Third-party dependency** | None | Public DHT nodes | Tor network | n0.computer relay | 524 | **Offline capability** | Yes (direct) | No (needs internet) | No (needs Tor circuit) | No (needs internet) | 525 | **Replay protection** | Nonce counter + message ID | Message ID + BEP44 seq + TTL | Message ID + replay cache | Message ID | 526 | **Mutual authentication** | Noise XX static keys | N/A (async) | Identity key exchange | QUIC + identity key mapping | 527 | **Max message size** | ~500 bytes per BLE packet (chunked) | ~950 bytes per slot (chunked) | Unlimited (WebSocket) | 100 MB | 528 529 --- 530 531 ## 10. Attack Surface Analysis 532 533 ### Scenario 1: Compromised Recipient Exchange Key 534 535 **Impact:** Attacker can decrypt any message encrypted to this key that they can intercept. 536 537 | Transport | Risk | Details | 538 |-----------|------|---------| 539 | BLE | Low | Requires physical proximity to intercept | 540 | DHT | High | Messages persist on public DHT for 48 hours; attacker can fetch and decrypt | 541 | Tor P2P | Medium | Must intercept in real-time (no persistence); Tor anonymity makes interception difficult | 542 | iroh | Medium | Must intercept in real-time via relay or network position | 543 544 **Mitigation:** Forward secrecy protects messages that have already been decrypted and whose ephemeral keys are gone. Only messages currently in transit or stored on the DHT are at risk. 545 546 ### Scenario 2: Compromised Sender Identity Key 547 548 **Impact:** Attacker can forge messages appearing to come from the compromised sender. 549 550 **All transports:** Cannot decrypt past messages (identity keys are for signing, not encryption). Can impersonate sender going forward until key is revoked/rotated. 551 552 ### Scenario 3: Global Passive Adversary (Network Observer) 553 554 | Transport | Observable | Not Observable | 555 |-----------|-----------|---------------| 556 | BLE | Radio signals within ~10m | Content (encrypted) | 557 | DHT | IP addresses, slot access patterns, timing | Message content, sender identity | 558 | Tor P2P | Tor circuit existence (not endpoints) | .onion addresses, message content, peer identities | 559 | iroh | IP addresses, relay connections, timing | Message content (double-encrypted) | 560 561 ### Scenario 4: Compromised Relay/Infrastructure 562 563 | Infrastructure | Attacker Capability | 564 |---------------|---------------------| 565 | DHT nodes | Store/serve ciphertext; cannot decrypt. Can refuse to store (DoS). | 566 | Tor relays | Route encrypted circuits; cannot decrypt. Guard node knows client IP. | 567 | n0 relay | See connection metadata and encrypted QUIC packets; cannot decrypt application-layer encryption. | 568 | BLE | N/A — no infrastructure | 569 570 ### Scenario 5: Nonce Reuse 571 572 ChaCha20-Poly1305 is catastrophically broken by nonce reuse with the same key. The system mitigates this through: 573 574 1. **Random 96-bit nonces** generated per message via OS CSPRNG 575 2. **Unique encryption keys** per message (HKDF with message_id salt ensures different key even if nonce collides) 576 3. **Noise transport** uses counter-based nonces (guaranteed unique within session) 577 578 **Residual risk:** Negligible. Birthday bound for 96-bit random nonces is 2^48 messages per key, and keys are never reused across messages. 579 580 --- 581 582 *This document describes the cryptographic architecture as implemented. No system is perfectly secure — security depends on correct implementation, secure key management, and operational practices. Regular security audits of the Rust cryptographic core are recommended.*