transport-security.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Ekko Transport Security Documentation</title> 7 <style> 8 @page { 9 size: A4; 10 margin: 2cm; 11 } 12 body { 13 font-family: -apple-system, 'Helvetica Neue', Arial, sans-serif; 14 line-height: 1.6; 15 color: #1a1a2e; 16 max-width: 800px; 17 margin: 0 auto; 18 padding: 20px; 19 font-size: 11pt; 20 } 21 h1 { 22 color: #0D0D18; 23 border-bottom: 3px solid #6C63FF; 24 padding-bottom: 10px; 25 font-size: 24pt; 26 page-break-before: avoid; 27 } 28 h2 { 29 color: #16213e; 30 border-bottom: 1px solid #ddd; 31 padding-bottom: 6px; 32 margin-top: 2em; 33 font-size: 16pt; 34 page-break-before: always; 35 } 36 h2:first-of-type { page-break-before: avoid; } 37 h3 { 38 color: #0f3460; 39 margin-top: 1.5em; 40 font-size: 13pt; 41 } 42 h4 { color: #533483; font-size: 11pt; } 43 table { 44 border-collapse: collapse; 45 width: 100%; 46 margin: 1em 0; 47 font-size: 10pt; 48 } 49 th, td { 50 border: 1px solid #ccc; 51 padding: 6px 10px; 52 text-align: left; 53 } 54 th { 55 background: #16213e; 56 color: white; 57 font-weight: 600; 58 } 59 tr:nth-child(even) { background: #f8f8fc; } 60 code { 61 background: #f0f0f5; 62 padding: 1px 5px; 63 border-radius: 3px; 64 font-family: 'SF Mono', 'Menlo', monospace; 65 font-size: 9.5pt; 66 } 67 pre { 68 background: #1a1a2e; 69 color: #e0e0e0; 70 padding: 14px; 71 border-radius: 6px; 72 overflow-x: auto; 73 font-size: 9pt; 74 line-height: 1.5; 75 } 76 pre code { background: none; color: inherit; padding: 0; } 77 .diagram-container { 78 margin: 1.5em 0; 79 text-align: center; 80 page-break-inside: avoid; 81 } 82 .diagram-container svg { 83 max-width: 100%; 84 height: auto; 85 } 86 .caption { 87 font-size: 9pt; 88 color: #666; 89 font-style: italic; 90 margin-top: 6px; 91 } 92 .note-box { 93 background: #f0f0ff; 94 border-left: 4px solid #6C63FF; 95 padding: 10px 14px; 96 margin: 1em 0; 97 border-radius: 0 4px 4px 0; 98 font-size: 10pt; 99 } 100 .warning-box { 101 background: #fff8f0; 102 border-left: 4px solid #e67e22; 103 padding: 10px 14px; 104 margin: 1em 0; 105 border-radius: 0 4px 4px 0; 106 font-size: 10pt; 107 } 108 .cover { 109 text-align: center; 110 padding: 120px 0 60px; 111 page-break-after: always; 112 } 113 .cover h1 { border: none; font-size: 36pt; color: #6C63FF; } 114 .cover .subtitle { font-size: 14pt; color: #666; margin-top: 10px; } 115 .cover .meta { font-size: 10pt; color: #999; margin-top: 40px; } 116 .toc { page-break-after: always; } 117 .toc a { color: #16213e; text-decoration: none; } 118 .toc li { margin: 6px 0; } 119 .severity-high { color: #c0392b; font-weight: 600; } 120 .severity-medium { color: #e67e22; font-weight: 600; } 121 .severity-low { color: #27ae60; font-weight: 600; } 122 </style> 123 </head> 124 <body> 125 126 <!-- COVER PAGE --> 127 <div class="cover"> 128 <h1>Ekko</h1> 129 <div class="subtitle">Transport Security Documentation</div> 130 <div style="margin-top: 30px;"> 131 <svg width="120" height="120" viewBox="0 0 120 120"> 132 <circle cx="60" cy="60" r="55" fill="none" stroke="#6C63FF" stroke-width="3"/> 133 <circle cx="60" cy="60" r="35" fill="none" stroke="#6C63FF" stroke-width="2" opacity="0.6"/> 134 <circle cx="60" cy="60" r="15" fill="#6C63FF"/> 135 <line x1="60" y1="5" x2="60" y2="25" stroke="#6C63FF" stroke-width="2"/> 136 <line x1="60" y1="95" x2="60" y2="115" stroke="#6C63FF" stroke-width="2"/> 137 <line x1="5" y1="60" x2="25" y2="60" stroke="#6C63FF" stroke-width="2"/> 138 <line x1="95" y1="60" x2="115" y2="60" stroke="#6C63FF" stroke-width="2"/> 139 </svg> 140 </div> 141 <div class="meta"> 142 Version 1.0 — March 2026<br> 143 Technical Reference — Confidential 144 </div> 145 </div> 146 147 <!-- TABLE OF CONTENTS --> 148 <div class="toc"> 149 <h2 style="page-break-before: avoid;">Table of Contents</h2> 150 <ol> 151 <li><a href="#overview">Overview</a></li> 152 <li><a href="#crypto">Core Cryptographic Primitives</a></li> 153 <li><a href="#pipeline">Message Encryption Pipeline</a></li> 154 <li><a href="#ble">BLE (Bluetooth Low Energy)</a></li> 155 <li><a href="#dht">DHT (Distributed Hash Table)</a></li> 156 <li><a href="#tor">Tor P2P (Onion-Routed Peer-to-Peer)</a></li> 157 <li><a href="#iroh">iroh (QUIC-Based P2P)</a></li> 158 <li><a href="#gossip">BLE Gossip (Proximity Relay)</a></li> 159 <li><a href="#matrix">Comparative Security Matrix</a></li> 160 <li><a href="#attacks">Attack Surface Analysis</a></li> 161 </ol> 162 </div> 163 164 <!-- 1. OVERVIEW --> 165 <h2 id="overview">1. Overview</h2> 166 167 <p>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.</p> 168 169 <div class="diagram-container"> 170 <svg width="700" height="320" viewBox="0 0 700 320"> 171 <!-- Title --> 172 <text x="350" y="25" text-anchor="middle" font-size="14" font-weight="bold" fill="#16213e">Transport Architecture Overview</text> 173 174 <!-- Sender device --> 175 <rect x="20" y="50" width="120" height="60" rx="8" fill="#6C63FF" opacity="0.15" stroke="#6C63FF" stroke-width="2"/> 176 <text x="80" y="75" text-anchor="middle" font-size="11" fill="#16213e" font-weight="bold">Sender</text> 177 <text x="80" y="92" text-anchor="middle" font-size="9" fill="#666">Device A</text> 178 179 <!-- Recipient device --> 180 <rect x="560" y="50" width="120" height="60" rx="8" fill="#6C63FF" opacity="0.15" stroke="#6C63FF" stroke-width="2"/> 181 <text x="620" y="75" text-anchor="middle" font-size="11" fill="#16213e" font-weight="bold">Recipient</text> 182 <text x="620" y="92" text-anchor="middle" font-size="9" fill="#666">Device B</text> 183 184 <!-- Encryption box --> 185 <rect x="20" y="130" width="120" height="40" rx="6" fill="#2ecc71" opacity="0.2" stroke="#2ecc71" stroke-width="1.5"/> 186 <text x="80" y="155" text-anchor="middle" font-size="9" fill="#16213e">App-Level Encrypt</text> 187 188 <!-- Decryption box --> 189 <rect x="560" y="130" width="120" height="40" rx="6" fill="#2ecc71" opacity="0.2" stroke="#2ecc71" stroke-width="1.5"/> 190 <text x="620" y="155" text-anchor="middle" font-size="9" fill="#16213e">App-Level Decrypt</text> 191 192 <!-- Arrow down from sender --> 193 <line x1="80" y1="110" x2="80" y2="130" stroke="#333" stroke-width="1.5" marker-end="url(#arrow)"/> 194 <!-- Arrow down from decrypt to recipient --> 195 196 <!-- Transport channels --> 197 <!-- BLE --> 198 <rect x="220" y="135" width="260" height="32" rx="6" fill="#3498db" opacity="0.15" stroke="#3498db" stroke-width="1.5"/> 199 <text x="350" y="155" text-anchor="middle" font-size="10" fill="#16213e">BLE (Noise XX + Direct)</text> 200 201 <!-- DHT --> 202 <rect x="220" y="177" width="260" height="32" rx="6" fill="#e67e22" opacity="0.15" stroke="#e67e22" stroke-width="1.5"/> 203 <text x="350" y="197" text-anchor="middle" font-size="10" fill="#16213e">DHT (BitTorrent Mainline + BEP44)</text> 204 205 <!-- Tor --> 206 <rect x="220" y="219" width="260" height="32" rx="6" fill="#9b59b6" opacity="0.15" stroke="#9b59b6" stroke-width="1.5"/> 207 <text x="350" y="239" text-anchor="middle" font-size="10" fill="#16213e">Tor P2P (Onion Service + WebSocket)</text> 208 209 <!-- iroh --> 210 <rect x="220" y="261" width="260" height="32" rx="6" fill="#1abc9c" opacity="0.15" stroke="#1abc9c" stroke-width="1.5"/> 211 <text x="350" y="281" text-anchor="middle" font-size="10" fill="#16213e">iroh (QUIC + n0 Relay)</text> 212 213 <!-- Arrows from encrypt to transports --> 214 <line x1="140" y1="150" x2="220" y2="150" stroke="#333" stroke-width="1" marker-end="url(#arrow)"/> 215 <line x1="140" y1="150" x2="220" y2="192" stroke="#333" stroke-width="1" marker-end="url(#arrow)"/> 216 <line x1="140" y1="150" x2="220" y2="234" stroke="#333" stroke-width="1" marker-end="url(#arrow)"/> 217 <line x1="140" y1="150" x2="220" y2="276" stroke="#333" stroke-width="1" marker-end="url(#arrow)"/> 218 219 <!-- Arrows from transports to decrypt --> 220 <line x1="480" y1="150" x2="560" y2="150" stroke="#333" stroke-width="1" marker-end="url(#arrow)"/> 221 <line x1="480" y1="192" x2="560" y2="150" stroke="#333" stroke-width="1" marker-end="url(#arrow)"/> 222 <line x1="480" y1="234" x2="560" y2="150" stroke="#333" stroke-width="1" marker-end="url(#arrow)"/> 223 <line x1="480" y1="276" x2="560" y2="150" stroke="#333" stroke-width="1" marker-end="url(#arrow)"/> 224 225 <!-- Priority label --> 226 <text x="350" y="308" text-anchor="middle" font-size="9" fill="#999">Priority: BLE → iroh → Tor P2P → DHT</text> 227 228 <defs> 229 <marker id="arrow" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"> 230 <polygon points="0 0, 8 3, 0 6" fill="#333"/> 231 </marker> 232 </defs> 233 </svg> 234 <div class="caption">Figure 1: Messages are encrypted once at the application layer, then delivered via the fastest available transport.</div> 235 </div> 236 237 <div class="note-box"> 238 <strong>Key Principle:</strong> 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. 239 </div> 240 241 <!-- 2. CRYPTO PRIMITIVES --> 242 <h2 id="crypto">2. Core Cryptographic Primitives</h2> 243 244 <p>All cryptographic operations are implemented in Rust using audited crates. No custom cryptography is used.</p> 245 246 <h3>Key Types</h3> 247 <table> 248 <tr><th>Key</th><th>Algorithm</th><th>Size</th><th>Lifetime</th><th>Purpose</th></tr> 249 <tr><td>Identity</td><td>Ed25519</td><td>32-byte secret + public</td><td>Device lifetime</td><td>Signing, authentication</td></tr> 250 <tr><td>Exchange</td><td>X25519</td><td>32-byte secret + public</td><td>Device lifetime</td><td>Static key agreement</td></tr> 251 <tr><td>Ephemeral</td><td>X25519</td><td>32-byte secret + public</td><td>Single message</td><td>Forward secrecy</td></tr> 252 </table> 253 254 <h3>Algorithms</h3> 255 <table> 256 <tr><th>Function</th><th>Algorithm</th><th>Parameters</th></tr> 257 <tr><td>Authenticated encryption</td><td>ChaCha20-Poly1305</td><td>256-bit key, 96-bit nonce, 128-bit tag</td></tr> 258 <tr><td>Key agreement</td><td>X25519 ECDH</td><td>32-byte shared secret output</td></tr> 259 <tr><td>Key derivation</td><td>HKDF-SHA256 (RFC 5869)</td><td>Domain prefix: <code>DeadDrop-v1-</code></td></tr> 260 <tr><td>Signatures</td><td>Ed25519</td><td>64-byte deterministic signatures</td></tr> 261 <tr><td>Fingerprints</td><td>BLAKE2b-256</td><td>32 bytes (128 bits displayed)</td></tr> 262 <tr><td>Memory cleanup</td><td>Zeroize</td><td>All secret types implement <code>ZeroizeOnDrop</code></td></tr> 263 </table> 264 265 <h3>Rust Crate Dependencies</h3> 266 <table> 267 <tr><th>Crate</th><th>Purpose</th></tr> 268 <tr><td><code>x25519-dalek</code></td><td>X25519 key agreement</td></tr> 269 <tr><td><code>ed25519-dalek</code></td><td>Ed25519 signatures</td></tr> 270 <tr><td><code>chacha20poly1305</code></td><td>AEAD encryption</td></tr> 271 <tr><td><code>hkdf</code> + <code>sha2</code></td><td>Key derivation</td></tr> 272 <tr><td><code>snow</code></td><td>Noise protocol framework</td></tr> 273 <tr><td><code>rand</code></td><td>OS-level CSPRNG</td></tr> 274 <tr><td><code>zeroize</code></td><td>Secure memory cleanup</td></tr> 275 </table> 276 277 <!-- 3. ENCRYPTION PIPELINE --> 278 <h2 id="pipeline">3. Message Encryption Pipeline</h2> 279 280 <p>Every message, regardless of transport, passes through the same encryption pipeline.</p> 281 282 <div class="diagram-container"> 283 <svg width="680" height="520" viewBox="0 0 680 520"> 284 <text x="340" y="22" text-anchor="middle" font-size="14" font-weight="bold" fill="#16213e">Message Encryption Pipeline</text> 285 286 <!-- Sender side --> 287 <text x="170" y="50" text-anchor="middle" font-size="12" font-weight="bold" fill="#6C63FF">Sender (Encrypt)</text> 288 289 <!-- Step 1: Plaintext --> 290 <rect x="60" y="60" width="220" height="30" rx="5" fill="#ecf0f1" stroke="#bdc3c7"/> 291 <text x="170" y="80" text-anchor="middle" font-size="9" fill="#333">PlaintextMessage {type, content, filename}</text> 292 293 <line x1="170" y1="90" x2="170" y2="108" stroke="#333" stroke-width="1" marker-end="url(#arrow2)"/> 294 295 <!-- Step 2: Generate IDs --> 296 <rect x="60" y="108" width="220" height="26" rx="5" fill="#dff9fb" stroke="#00b894"/> 297 <text x="170" y="125" text-anchor="middle" font-size="9" fill="#333">Generate message_id (16 random bytes)</text> 298 299 <line x1="170" y1="134" x2="170" y2="150" stroke="#333" stroke-width="1" marker-end="url(#arrow2)"/> 300 301 <!-- Step 3: Ephemeral key --> 302 <rect x="60" y="150" width="220" height="26" rx="5" fill="#dff9fb" stroke="#00b894"/> 303 <text x="170" y="167" text-anchor="middle" font-size="9" fill="#333">Generate ephemeral X25519 key pair</text> 304 305 <line x1="170" y1="176" x2="170" y2="192" stroke="#333" stroke-width="1" marker-end="url(#arrow2)"/> 306 307 <!-- Step 4: ECDH --> 308 <rect x="60" y="192" width="220" height="35" rx="5" fill="#ffeaa7" stroke="#fdcb6e"/> 309 <text x="170" y="207" text-anchor="middle" font-size="9" fill="#333" font-weight="bold">X25519 ECDH</text> 310 <text x="170" y="220" text-anchor="middle" font-size="8" fill="#666">ephemeral_secret × recipient_exchange_pub</text> 311 312 <line x1="170" y1="227" x2="170" y2="243" stroke="#333" stroke-width="1" marker-end="url(#arrow2)"/> 313 314 <!-- Step 5: HKDF --> 315 <rect x="60" y="243" width="220" height="35" rx="5" fill="#ffeaa7" stroke="#fdcb6e"/> 316 <text x="170" y="258" text-anchor="middle" font-size="9" fill="#333" font-weight="bold">HKDF-SHA256</text> 317 <text x="170" y="271" text-anchor="middle" font-size="8" fill="#666">salt=msg_id, info="DeadDrop-v1-message-key"</text> 318 319 <line x1="170" y1="278" x2="170" y2="294" stroke="#333" stroke-width="1" marker-end="url(#arrow2)"/> 320 321 <!-- Step 6: AEAD --> 322 <rect x="60" y="294" width="220" height="35" rx="5" fill="#fab1a0" stroke="#e17055"/> 323 <text x="170" y="309" text-anchor="middle" font-size="9" fill="#333" font-weight="bold">ChaCha20-Poly1305</text> 324 <text x="170" y="322" text-anchor="middle" font-size="8" fill="#666">key, random_nonce, plaintext, aad=msg_id</text> 325 326 <line x1="170" y1="329" x2="170" y2="345" stroke="#333" stroke-width="1" marker-end="url(#arrow2)"/> 327 328 <!-- Step 7: Sign --> 329 <rect x="60" y="345" width="220" height="35" rx="5" fill="#a29bfe" stroke="#6c5ce7"/> 330 <text x="170" y="360" text-anchor="middle" font-size="9" fill="#fff" font-weight="bold">Ed25519 Sign</text> 331 <text x="170" y="373" text-anchor="middle" font-size="8" fill="#ddd">identity_secret(message_id ∥ ciphertext)</text> 332 333 <line x1="170" y1="380" x2="170" y2="396" stroke="#333" stroke-width="1" marker-end="url(#arrow2)"/> 334 335 <!-- Result --> 336 <rect x="40" y="396" width="260" height="80" rx="6" fill="#16213e" stroke="#16213e"/> 337 <text x="170" y="415" text-anchor="middle" font-size="10" fill="#6C63FF" font-weight="bold">EncryptedMessage</text> 338 <text x="170" y="432" text-anchor="middle" font-size="8" fill="#ccc">message_id [16B] · ephemeral_pub [32B]</text> 339 <text x="170" y="445" text-anchor="middle" font-size="8" fill="#ccc">nonce [12B] · ciphertext [var + 16B tag]</text> 340 <text x="170" y="458" text-anchor="middle" font-size="8" fill="#ccc">timestamp [8B] · signature [64B]</text> 341 342 <!-- Divider --> 343 <line x1="340" y1="45" x2="340" y2="495" stroke="#ddd" stroke-width="1" stroke-dasharray="5,3"/> 344 345 <!-- Recipient side --> 346 <text x="510" y="50" text-anchor="middle" font-size="12" font-weight="bold" fill="#6C63FF">Recipient (Decrypt)</text> 347 348 <!-- R Step 1 --> 349 <rect x="400" y="60" width="220" height="30" rx="5" fill="#16213e" stroke="#16213e"/> 350 <text x="510" y="80" text-anchor="middle" font-size="9" fill="#6C63FF">EncryptedMessage received</text> 351 352 <line x1="510" y1="90" x2="510" y2="118" stroke="#333" stroke-width="1" marker-end="url(#arrow2)"/> 353 354 <!-- R Step 2: Verify --> 355 <rect x="400" y="118" width="220" height="35" rx="5" fill="#a29bfe" stroke="#6c5ce7"/> 356 <text x="510" y="133" text-anchor="middle" font-size="9" fill="#fff" font-weight="bold">Ed25519 Verify</text> 357 <text x="510" y="146" text-anchor="middle" font-size="8" fill="#ddd">sender_pub(message_id ∥ ciphertext, sig)</text> 358 359 <line x1="510" y1="153" x2="510" y2="173" stroke="#333" stroke-width="1" marker-end="url(#arrow2)"/> 360 361 <!-- Reject --> 362 <rect x="635" y="125" width="45" height="20" rx="3" fill="#e74c3c" stroke="#c0392b"/> 363 <text x="657" y="139" text-anchor="middle" font-size="8" fill="#fff">Reject</text> 364 <line x1="620" y1="135" x2="635" y2="135" stroke="#e74c3c" stroke-width="1" marker-end="url(#arrow2)"/> 365 366 <!-- R Step 3: ECDH --> 367 <rect x="400" y="173" width="220" height="35" rx="5" fill="#ffeaa7" stroke="#fdcb6e"/> 368 <text x="510" y="188" text-anchor="middle" font-size="9" fill="#333" font-weight="bold">X25519 ECDH</text> 369 <text x="510" y="201" text-anchor="middle" font-size="8" fill="#666">recipient_exchange_secret × ephemeral_pub</text> 370 371 <line x1="510" y1="208" x2="510" y2="226" stroke="#333" stroke-width="1" marker-end="url(#arrow2)"/> 372 373 <!-- R Step 4: HKDF --> 374 <rect x="400" y="226" width="220" height="35" rx="5" fill="#ffeaa7" stroke="#fdcb6e"/> 375 <text x="510" y="241" text-anchor="middle" font-size="9" fill="#333" font-weight="bold">HKDF-SHA256</text> 376 <text x="510" y="254" text-anchor="middle" font-size="8" fill="#666">same parameters → same key</text> 377 378 <line x1="510" y1="261" x2="510" y2="279" stroke="#333" stroke-width="1" marker-end="url(#arrow2)"/> 379 380 <!-- R Step 5: Decrypt --> 381 <rect x="400" y="279" width="220" height="35" rx="5" fill="#fab1a0" stroke="#e17055"/> 382 <text x="510" y="294" text-anchor="middle" font-size="9" fill="#333" font-weight="bold">ChaCha20-Poly1305 Decrypt</text> 383 <text x="510" y="307" text-anchor="middle" font-size="8" fill="#666">verify tag → reject if tampered</text> 384 385 <line x1="510" y1="314" x2="510" y2="340" stroke="#333" stroke-width="1" marker-end="url(#arrow2)"/> 386 387 <!-- R Result --> 388 <rect x="400" y="340" width="220" height="30" rx="5" fill="#ecf0f1" stroke="#bdc3c7"/> 389 <text x="510" y="360" text-anchor="middle" font-size="9" fill="#333">PlaintextMessage</text> 390 391 <!-- Forward secrecy note --> 392 <rect x="370" y="400" width="280" height="80" rx="6" fill="#dff9fb" stroke="#00b894" stroke-width="1.5"/> 393 <text x="510" y="420" text-anchor="middle" font-size="9" fill="#00695c" font-weight="bold">Forward Secrecy</text> 394 <text x="510" y="436" text-anchor="middle" font-size="8" fill="#333">Ephemeral secret consumed by Rust</text> 395 <text x="510" y="449" text-anchor="middle" font-size="8" fill="#333">ownership after DH, then zeroized.</text> 396 <text x="510" y="462" text-anchor="middle" font-size="8" fill="#333">Past messages cannot be decrypted</text> 397 <text x="510" y="475" text-anchor="middle" font-size="8" fill="#333">even if exchange key is compromised.</text> 398 399 <defs> 400 <marker id="arrow2" markerWidth="7" markerHeight="5" refX="7" refY="2.5" orient="auto"> 401 <polygon points="0 0, 7 2.5, 0 5" fill="#333"/> 402 </marker> 403 </defs> 404 </svg> 405 <div class="caption">Figure 2: The shared encryption pipeline used by all transports. Sender encrypts with ephemeral ECDH; recipient decrypts with their static exchange key.</div> 406 </div> 407 408 <div class="warning-box"> 409 <strong>Limitation:</strong> There is no Double Ratchet or evolving session state. Each message is encrypted independently. If the recipient's exchange key is compromised <em>before</em> they decrypt a message still in transit, that message can be decrypted by the attacker. 410 </div> 411 412 <!-- 4. BLE --> 413 <h2 id="ble">4. BLE (Bluetooth Low Energy)</h2> 414 415 <h3>Architecture</h3> 416 <p>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.</p> 417 418 <h3>Third-Party Services</h3> 419 <p><strong>None.</strong> BLE operates entirely peer-to-peer with no internet connectivity required.</p> 420 421 <h3>Encryption Layers</h3> 422 <table> 423 <tr><th>Layer</th><th>Protocol</th><th>Purpose</th></tr> 424 <tr><td>Transport</td><td>Noise_XX_25519_ChaChaPoly_SHA256</td><td>Session encryption, mutual authentication</td></tr> 425 <tr><td>Application</td><td>ChaCha20-Poly1305 + Ed25519</td><td>Per-message encryption (§3)</td></tr> 426 </table> 427 428 <h3>Noise XX Handshake</h3> 429 430 <div class="diagram-container"> 431 <svg width="600" height="350" viewBox="0 0 600 350"> 432 <text x="300" y="22" text-anchor="middle" font-size="13" font-weight="bold" fill="#16213e">Noise XX Handshake Protocol</text> 433 434 <!-- Initiator --> 435 <rect x="40" y="40" width="130" height="35" rx="6" fill="#3498db" opacity="0.2" stroke="#3498db" stroke-width="1.5"/> 436 <text x="105" y="62" text-anchor="middle" font-size="10" font-weight="bold" fill="#16213e">Initiator (Alice)</text> 437 <line x1="105" y1="75" x2="105" y2="330" stroke="#3498db" stroke-width="1.5"/> 438 439 <!-- Responder --> 440 <rect x="430" y="40" width="130" height="35" rx="6" fill="#e74c3c" opacity="0.2" stroke="#e74c3c" stroke-width="1.5"/> 441 <text x="495" y="62" text-anchor="middle" font-size="10" font-weight="bold" fill="#16213e">Responder (Bob)</text> 442 <line x1="495" y1="75" x2="495" y2="330" stroke="#e74c3c" stroke-width="1.5"/> 443 444 <!-- Message 1 --> 445 <line x1="115" y1="110" x2="485" y2="110" stroke="#333" stroke-width="1.5" marker-end="url(#arrow2)"/> 446 <rect x="200" y="93" width="200" height="24" rx="4" fill="#fff" stroke="#ccc"/> 447 <text x="300" y="109" text-anchor="middle" font-size="9" fill="#333" font-weight="bold">MSG 1: → e</text> 448 <text x="300" y="130" text-anchor="middle" font-size="8" fill="#999">32-byte ephemeral public key (unencrypted)</text> 449 450 <!-- Message 2 --> 451 <line x1="485" y1="170" x2="115" y2="170" stroke="#333" stroke-width="1.5" marker-end="url(#arrow2)"/> 452 <rect x="180" y="153" width="240" height="24" rx="4" fill="#fff" stroke="#ccc"/> 453 <text x="300" y="169" text-anchor="middle" font-size="9" fill="#333" font-weight="bold">MSG 2: ← e, ee, s, es</text> 454 <text x="300" y="190" text-anchor="middle" font-size="8" fill="#999">Bob's ephemeral + encrypted static key (80B)</text> 455 456 <!-- Message 3 --> 457 <line x1="115" y1="230" x2="485" y2="230" stroke="#333" stroke-width="1.5" marker-end="url(#arrow2)"/> 458 <rect x="200" y="213" width="200" height="24" rx="4" fill="#fff" stroke="#ccc"/> 459 <text x="300" y="229" text-anchor="middle" font-size="9" fill="#333" font-weight="bold">MSG 3: → s, se</text> 460 <text x="300" y="250" text-anchor="middle" font-size="8" fill="#999">Alice's encrypted static key (48B)</text> 461 462 <!-- Complete --> 463 <rect x="170" y="275" width="260" height="35" rx="6" fill="#2ecc71" opacity="0.2" stroke="#2ecc71" stroke-width="1.5"/> 464 <text x="300" y="290" text-anchor="middle" font-size="10" fill="#16213e" font-weight="bold">Symmetric Transport Established</text> 465 <text x="300" y="303" text-anchor="middle" font-size="8" fill="#666">Separate keys per direction, counter-based nonces</text> 466 </svg> 467 <div class="caption">Figure 3: Noise XX provides mutual authentication with encrypted static key exchange.</div> 468 </div> 469 470 <h3>Forward Secrecy</h3> 471 <p>BLE provides forward secrecy at two levels:</p> 472 <ol> 473 <li><strong>Session level</strong> (Noise XX): Each BLE session generates fresh ephemeral keys. Compromise of static keys does not reveal past session keys.</li> 474 <li><strong>Message level</strong> (§3): Each message within a session uses independent ephemeral ECDH.</li> 475 </ol> 476 477 <h3>Security Risks</h3> 478 <table> 479 <tr><th>Risk</th><th>Severity</th><th>Notes</th></tr> 480 <tr><td>BLE range limitation</td><td class="severity-low">Low</td><td>~10m range limits interception to physical proximity</td></tr> 481 <tr><td>BLE advertising metadata</td><td class="severity-low">Low</td><td>Rotating IDs derived via HKDF prevent device tracking</td></tr> 482 <tr><td>MITM relay attack</td><td class="severity-low">Low</td><td>Noise XX mutual authentication prevents real-time key substitution</td></tr> 483 <tr><td>Radio jamming / DoS</td><td class="severity-medium">Medium</td><td>Radio-level jamming can prevent BLE communication</td></tr> 484 </table> 485 486 <!-- 5. DHT --> 487 <h2 id="dht">5. DHT (Distributed Hash Table)</h2> 488 489 <h3>Architecture</h3> 490 <p>Messages are encrypted, published to the public BitTorrent Mainline DHT as BEP44 mutable items, and fetched by recipients who poll their designated slots.</p> 491 492 <h3>Third-Party Services</h3> 493 <table> 494 <tr><th>Service</th><th>Operator</th><th>Role</th></tr> 495 <tr><td>Bootstrap nodes</td><td>BitTorrent, µTorrent, Transmission, libtorrent, CMU</td><td>Initial DHT network entry</td></tr> 496 <tr><td>DHT nodes</td><td>Thousands of public nodes</td><td>Store and relay BEP44 mutable items</td></tr> 497 </table> 498 499 <div class="note-box"> 500 <strong>No Ekko-specific infrastructure is required.</strong> The DHT is the same public network used by BitTorrent clients worldwide. No central servers, no relay services, no accounts. 501 </div> 502 503 <div class="diagram-container"> 504 <svg width="680" height="300" viewBox="0 0 680 300"> 505 <text x="340" y="22" text-anchor="middle" font-size="13" font-weight="bold" fill="#16213e">DHT Message Flow</text> 506 507 <!-- Sender --> 508 <rect x="20" y="50" width="100" height="50" rx="6" fill="#e67e22" opacity="0.2" stroke="#e67e22" stroke-width="1.5"/> 509 <text x="70" y="72" text-anchor="middle" font-size="10" font-weight="bold" fill="#16213e">Sender</text> 510 <text x="70" y="88" text-anchor="middle" font-size="8" fill="#666">Device A</text> 511 512 <!-- Encrypt --> 513 <rect x="150" y="55" width="90" height="40" rx="4" fill="#2ecc71" opacity="0.2" stroke="#2ecc71"/> 514 <text x="195" y="72" text-anchor="middle" font-size="8" fill="#333">Encrypt</text> 515 <text x="195" y="84" text-anchor="middle" font-size="7" fill="#666">(§3 pipeline)</text> 516 <line x1="120" y1="75" x2="150" y2="75" stroke="#333" stroke-width="1" marker-end="url(#arrow2)"/> 517 518 <!-- Wrap --> 519 <rect x="270" y="45" width="120" height="60" rx="4" fill="#e67e22" opacity="0.1" stroke="#e67e22"/> 520 <text x="330" y="63" text-anchor="middle" font-size="8" fill="#333" font-weight="bold">DhtStoredMessage</text> 521 <text x="330" y="76" text-anchor="middle" font-size="7" fill="#666">+ sender_key</text> 522 <text x="330" y="89" text-anchor="middle" font-size="7" fill="#666">+ expires_at (48h)</text> 523 <line x1="240" y1="75" x2="270" y2="75" stroke="#333" stroke-width="1" marker-end="url(#arrow2)"/> 524 525 <!-- DHT Cloud --> 526 <ellipse cx="340" cy="180" rx="130" ry="50" fill="#ecf0f1" stroke="#bdc3c7" stroke-width="1.5"/> 527 <text x="340" y="170" text-anchor="middle" font-size="11" font-weight="bold" fill="#16213e">Mainline DHT</text> 528 <text x="340" y="185" text-anchor="middle" font-size="8" fill="#666">BEP44 Mutable Items</text> 529 <text x="340" y="198" text-anchor="middle" font-size="7" fill="#999">Ed25519 signed · Slots 0-100</text> 530 531 <!-- Publish arrow --> 532 <line x1="330" y1="105" x2="330" y2="130" stroke="#e67e22" stroke-width="1.5" marker-end="url(#arrow2)"/> 533 <text x="308" y="120" font-size="8" fill="#e67e22">publish</text> 534 535 <!-- Fetch arrow --> 536 <line x1="370" y1="130" x2="370" y2="105" stroke="#3498db" stroke-width="1.5" marker-end="url(#arrow2)"/> 537 538 <!-- Recipient fetch --> 539 <rect x="420" y="45" width="120" height="60" rx="4" fill="#3498db" opacity="0.1" stroke="#3498db"/> 540 <text x="480" y="63" text-anchor="middle" font-size="8" fill="#333" font-weight="bold">Poll Slots</text> 541 <text x="480" y="76" text-anchor="middle" font-size="7" fill="#666">30s fast / 5min slow</text> 542 <text x="480" y="89" text-anchor="middle" font-size="7" fill="#666">concurrent fetch</text> 543 544 <!-- Decrypt --> 545 <rect x="560" y="55" width="90" height="40" rx="4" fill="#2ecc71" opacity="0.2" stroke="#2ecc71"/> 546 <text x="605" y="72" text-anchor="middle" font-size="8" fill="#333">Decrypt</text> 547 <text x="605" y="84" text-anchor="middle" font-size="7" fill="#666">(§3 pipeline)</text> 548 <line x1="540" y1="75" x2="560" y2="75" stroke="#333" stroke-width="1" marker-end="url(#arrow2)"/> 549 550 <!-- Slot detail --> 551 <rect x="80" y="240" width="520" height="45" rx="6" fill="#f8f8fc" stroke="#ddd"/> 552 <text x="340" y="258" text-anchor="middle" font-size="9" fill="#16213e" font-weight="bold">Slot Key Derivation</text> 553 <text x="340" y="275" text-anchor="middle" font-size="8" fill="#666">ed25519_seed = SHA-512("deaddrop/dht/ed25519/v1" ∥ recipient_exchange_public)[0..32] → salt = "deaddrop/v1/slot/{N}"</text> 554 </svg> 555 <div class="caption">Figure 4: DHT publish/fetch flow. Messages persist as BEP44 mutable items on the public BitTorrent DHT.</div> 556 </div> 557 558 <h3>Forward Secrecy</h3> 559 <p>Per-message forward secrecy is provided by the application-level encryption (§3). Each message uses an independent ephemeral ECDH key agreement.</p> 560 561 <div class="warning-box"> 562 <strong>DHT-specific limitation:</strong> 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. 563 </div> 564 565 <h3>Security Risks</h3> 566 <table> 567 <tr><th>Risk</th><th>Severity</th><th>Notes</th></tr> 568 <tr><td>Slot enumeration</td><td class="severity-medium">Medium</td><td>Observers can correlate slots 0–100 to a single recipient by access patterns</td></tr> 569 <tr><td>Volume analysis</td><td class="severity-medium">Medium</td><td>Number of occupied slots reveals approximate message count</td></tr> 570 <tr><td>IP address exposure</td><td class="severity-medium">Medium</td><td>DHT participation reveals the device's IP to DHT peers</td></tr> 571 <tr><td>Timing correlation</td><td class="severity-medium">Medium</td><td>Publish/fetch timing can correlate sender and recipient</td></tr> 572 <tr><td>No confidentiality at rest on DHT</td><td class="severity-low">Low</td><td>Mitigated by application-level encryption; nodes see only ciphertext</td></tr> 573 <tr><td>Stale message replay</td><td class="severity-low">Low</td><td>TTL (48h), message ID dedup, BEP44 sequence numbers prevent replay</td></tr> 574 </table> 575 576 <!-- 6. TOR --> 577 <h2 id="tor">6. Tor P2P (Onion-Routed Peer-to-Peer)</h2> 578 579 <h3>Architecture</h3> 580 <p>Devices create Tor v3 onion 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.</p> 581 582 <h3>Third-Party Services</h3> 583 <table> 584 <tr><th>Service</th><th>Operator</th><th>Role</th></tr> 585 <tr><td>Tor network</td><td>The Tor Project (volunteer-operated)</td><td>Onion routing, 3-hop circuit encryption</td></tr> 586 <tr><td>Directory authorities</td><td>The Tor Project</td><td>Network consensus</td></tr> 587 <tr><td>Mainline DHT</td><td>Public</td><td>.onion address rendezvous</td></tr> 588 </table> 589 590 <div class="note-box"> 591 <strong>No exit nodes are used.</strong> All connections are .onion-to-.onion (hidden service to hidden service), remaining entirely within the Tor network. This provides the strongest anonymity of any Ekko transport. 592 </div> 593 594 <h3>Encryption Layers</h3> 595 <table> 596 <tr><th>Layer</th><th>Protocol</th><th>Purpose</th></tr> 597 <tr><td>Network</td><td>Tor v3 onion routing</td><td>Anonymity, 3-hop circuit encryption</td></tr> 598 <tr><td>Transport</td><td>WebSocket over TCP</td><td>Message framing</td></tr> 599 <tr><td>Application</td><td>ChaCha20-Poly1305 + Ed25519</td><td>Per-message encryption (§3)</td></tr> 600 </table> 601 602 <div class="diagram-container"> 603 <svg width="680" height="300" viewBox="0 0 680 300"> 604 <text x="340" y="22" text-anchor="middle" font-size="13" font-weight="bold" fill="#16213e">Tor P2P Connection Architecture</text> 605 606 <!-- Device A --> 607 <rect x="20" y="60" width="90" height="45" rx="6" fill="#9b59b6" opacity="0.2" stroke="#9b59b6" stroke-width="1.5"/> 608 <text x="65" y="80" text-anchor="middle" font-size="9" font-weight="bold" fill="#16213e">Device A</text> 609 <text x="65" y="95" text-anchor="middle" font-size="7" fill="#666">SOCKS5:9050</text> 610 611 <!-- Guard A --> 612 <circle cx="170" cy="82" r="18" fill="#9b59b6" opacity="0.15" stroke="#9b59b6"/> 613 <text x="170" y="86" text-anchor="middle" font-size="7" fill="#333">Guard</text> 614 615 <!-- Middle --> 616 <circle cx="250" cy="82" r="18" fill="#9b59b6" opacity="0.15" stroke="#9b59b6"/> 617 <text x="250" y="86" text-anchor="middle" font-size="7" fill="#333">Relay</text> 618 619 <!-- Rendezvous --> 620 <circle cx="340" cy="82" r="22" fill="#9b59b6" opacity="0.25" stroke="#9b59b6" stroke-width="1.5"/> 621 <text x="340" y="80" text-anchor="middle" font-size="7" fill="#333" font-weight="bold">Rende-</text> 622 <text x="340" y="90" text-anchor="middle" font-size="7" fill="#333" font-weight="bold">zvous</text> 623 624 <!-- Middle B --> 625 <circle cx="430" cy="82" r="18" fill="#9b59b6" opacity="0.15" stroke="#9b59b6"/> 626 <text x="430" y="86" text-anchor="middle" font-size="7" fill="#333">Relay</text> 627 628 <!-- Guard B --> 629 <circle cx="510" cy="82" r="18" fill="#9b59b6" opacity="0.15" stroke="#9b59b6"/> 630 <text x="510" y="86" text-anchor="middle" font-size="7" fill="#333">Guard</text> 631 632 <!-- Device B --> 633 <rect x="570" y="60" width="90" height="45" rx="6" fill="#9b59b6" opacity="0.2" stroke="#9b59b6" stroke-width="1.5"/> 634 <text x="615" y="80" text-anchor="middle" font-size="9" font-weight="bold" fill="#16213e">Device B</text> 635 <text x="615" y="95" text-anchor="middle" font-size="7" fill="#666">.onion:80</text> 636 637 <!-- Connection lines --> 638 <line x1="110" y1="82" x2="152" y2="82" stroke="#9b59b6" stroke-width="1.5"/> 639 <line x1="188" y1="82" x2="232" y2="82" stroke="#9b59b6" stroke-width="1.5"/> 640 <line x1="268" y1="82" x2="318" y2="82" stroke="#9b59b6" stroke-width="1.5"/> 641 <line x1="362" y1="82" x2="412" y2="82" stroke="#9b59b6" stroke-width="1.5"/> 642 <line x1="448" y1="82" x2="492" y2="82" stroke="#9b59b6" stroke-width="1.5"/> 643 <line x1="528" y1="82" x2="570" y2="82" stroke="#9b59b6" stroke-width="1.5"/> 644 645 <!-- Encryption layers --> 646 <rect x="80" y="130" width="520" height="25" rx="4" fill="#9b59b6" opacity="0.08" stroke="#9b59b6" stroke-width="0.5"/> 647 <text x="340" y="146" text-anchor="middle" font-size="8" fill="#9b59b6">Tor Circuit Encryption (3 layers of symmetric encryption, per-hop)</text> 648 649 <rect x="80" y="160" width="520" height="25" rx="4" fill="#3498db" opacity="0.08" stroke="#3498db" stroke-width="0.5"/> 650 <text x="340" y="176" text-anchor="middle" font-size="8" fill="#3498db">WebSocket Transport (framing + identity key exchange)</text> 651 652 <rect x="80" y="190" width="520" height="25" rx="4" fill="#2ecc71" opacity="0.08" stroke="#2ecc71" stroke-width="0.5"/> 653 <text x="340" y="206" text-anchor="middle" font-size="8" fill="#2ecc71">Application Encryption (ChaCha20-Poly1305 + Ed25519, per-message ephemeral ECDH)</text> 654 655 <!-- DHT Rendezvous --> 656 <rect x="180" y="240" width="320" height="45" rx="6" fill="#e67e22" opacity="0.1" stroke="#e67e22"/> 657 <text x="340" y="258" text-anchor="middle" font-size="9" fill="#16213e" font-weight="bold">DHT Rendezvous</text> 658 <text x="340" y="275" text-anchor="middle" font-size="8" fill="#666">salt="deaddrop/v1/rendezvous" · value=.onion address · 1-hour TTL</text> 659 </svg> 660 <div class="caption">Figure 5: Tor P2P uses .onion-to-.onion connections (no exit nodes). Peer discovery via DHT rendezvous.</div> 661 </div> 662 663 <h3>Forward Secrecy</h3> 664 <p>Per-message forward secrecy via application-level encryption (§3). Additionally, Tor provides circuit-level forward secrecy with ephemeral DH at each hop.</p> 665 666 <h3>Security Risks</h3> 667 <table> 668 <tr><th>Risk</th><th>Severity</th><th>Notes</th></tr> 669 <tr><td>DHT rendezvous metadata</td><td class="severity-medium">Medium</td><td>DHT queries for .onion addresses are not anonymous</td></tr> 670 <tr><td>Timing correlation</td><td class="severity-medium">Medium</td><td>Visible to ISP-level observers watching both endpoints</td></tr> 671 <tr><td>Message size analysis</td><td class="severity-low">Low</td><td>No padding; file transfer sizes observable</td></tr> 672 <tr><td>Tor circuit compromise</td><td class="severity-low">Low</td><td>Requires control of guard + rendezvous point</td></tr> 673 <tr><td>Stale .onion address</td><td class="severity-low">Low</td><td>1-hour TTL prevents use of expired addresses</td></tr> 674 </table> 675 676 <!-- 7. IROH --> 677 <h2 id="iroh">7. iroh (QUIC-Based P2P)</h2> 678 679 <h3>Architecture</h3> 680 <p>iroh is a QUIC-based P2P library from n0.computer providing NAT traversal via relay servers and optional UDP hole-punching. Ekko uses iroh with custom ALPN protocol <code>deaddrop/msg/v1</code> for push-based message delivery.</p> 681 682 <h3>Third-Party Services</h3> 683 <table> 684 <tr><th>Service</th><th>Operator</th><th>Role</th></tr> 685 <tr><td>n0.computer relay servers</td><td>n0 Inc</td><td>QUIC relay for NAT traversal</td></tr> 686 <tr><td>Mainline DHT</td><td>Public</td><td>EndpointId rendezvous</td></tr> 687 </table> 688 689 <h3>Encryption Layers (Double Encryption)</h3> 690 <table> 691 <tr><th>Layer</th><th>Protocol</th><th>Purpose</th></tr> 692 <tr><td>Transport</td><td>QUIC (TLS 1.3, ChaCha20-Poly1305)</td><td>Transport confidentiality</td></tr> 693 <tr><td>Application</td><td>ChaCha20-Poly1305 + Ed25519</td><td>Per-message encryption (§3)</td></tr> 694 </table> 695 696 <div class="diagram-container"> 697 <svg width="680" height="280" viewBox="0 0 680 280"> 698 <text x="340" y="22" text-anchor="middle" font-size="13" font-weight="bold" fill="#16213e">iroh Connection & Encryption</text> 699 700 <!-- Device A --> 701 <rect x="20" y="60" width="100" height="50" rx="6" fill="#1abc9c" opacity="0.2" stroke="#1abc9c" stroke-width="1.5"/> 702 <text x="70" y="80" text-anchor="middle" font-size="9" font-weight="bold" fill="#16213e">Device A</text> 703 <text x="70" y="95" text-anchor="middle" font-size="7" fill="#666">EndpointId: abc...</text> 704 705 <!-- Device B --> 706 <rect x="560" y="60" width="100" height="50" rx="6" fill="#1abc9c" opacity="0.2" stroke="#1abc9c" stroke-width="1.5"/> 707 <text x="610" y="80" text-anchor="middle" font-size="9" font-weight="bold" fill="#16213e">Device B</text> 708 <text x="610" y="95" text-anchor="middle" font-size="7" fill="#666">EndpointId: def...</text> 709 710 <!-- Direct path --> 711 <line x1="120" y1="72" x2="560" y2="72" stroke="#2ecc71" stroke-width="2" stroke-dasharray="6,3"/> 712 <text x="340" y="65" text-anchor="middle" font-size="8" fill="#2ecc71" font-weight="bold">Direct UDP (hole-punched)</text> 713 714 <!-- Relay --> 715 <rect x="270" y="110" width="140" height="40" rx="6" fill="#e74c3c" opacity="0.1" stroke="#e74c3c"/> 716 <text x="340" y="128" text-anchor="middle" font-size="9" font-weight="bold" fill="#16213e">n0 Relay</text> 717 <text x="340" y="142" text-anchor="middle" font-size="7" fill="#666">Fallback if NAT blocks UDP</text> 718 719 <line x1="120" y1="95" x2="270" y2="125" stroke="#e74c3c" stroke-width="1" stroke-dasharray="4,3"/> 720 <line x1="410" y1="125" x2="560" y2="95" stroke="#e74c3c" stroke-width="1" stroke-dasharray="4,3"/> 721 722 <!-- Encryption layers --> 723 <rect x="60" y="170" width="560" height="22" rx="3" fill="#1abc9c" opacity="0.1" stroke="#1abc9c"/> 724 <text x="340" y="185" text-anchor="middle" font-size="8" fill="#1abc9c">QUIC / TLS 1.3 Encryption (transport layer — protects from relay)</text> 725 726 <rect x="60" y="197" width="560" height="22" rx="3" fill="#2ecc71" opacity="0.1" stroke="#2ecc71"/> 727 <text x="340" y="212" text-anchor="middle" font-size="8" fill="#2ecc71">Application Encryption (ChaCha20-Poly1305 + Ed25519, per-message ECDH)</text> 728 729 <!-- What relay sees --> 730 <rect x="140" y="235" width="400" height="35" rx="6" fill="#fff8f0" stroke="#e67e22"/> 731 <text x="340" y="250" text-anchor="middle" font-size="9" fill="#e67e22" font-weight="bold">Relay can see: connection patterns, IPs, packet sizes, timing</text> 732 <text x="340" y="263" text-anchor="middle" font-size="8" fill="#999">Cannot see: message content (double-encrypted)</text> 733 </svg> 734 <div class="caption">Figure 6: iroh provides double encryption. The relay sees only encrypted QUIC packets and cannot access message content.</div> 735 </div> 736 737 <h3>Forward Secrecy</h3> 738 <p>Per-message forward secrecy via application-level encryption (§3). QUIC/TLS 1.3 adds transport-level forward secrecy with ephemeral ECDH per connection.</p> 739 740 <h3>Security Risks</h3> 741 <table> 742 <tr><th>Risk</th><th>Severity</th><th>Notes</th></tr> 743 <tr><td>Relay metadata visibility</td><td class="severity-medium">Medium</td><td>n0 relay sees connection patterns and IP addresses</td></tr> 744 <tr><td>IP address exposure</td><td class="severity-medium">Medium</td><td>Both peers' IPs visible to relay; no anonymity layer</td></tr> 745 <tr><td>DHT endpoint visibility</td><td class="severity-low">Low</td><td>Published EndpointIds are publicly queryable</td></tr> 746 <tr><td>Message size analysis</td><td class="severity-low">Low</td><td>Packet sizes visible to relay and network observers</td></tr> 747 <tr><td>Endpoint impersonation</td><td class="severity-low">Low</td><td>Ed25519 signature verification prevents message forgery</td></tr> 748 </table> 749 750 <!-- 8. GOSSIP --> 751 <h2 id="gossip">8. BLE Gossip (Proximity Relay)</h2> 752 753 <h3>Architecture</h3> 754 <p>BLE Gossip extends the BLE transport to relay messages between devices that don't share contacts. After a standard BLE exchange, devices enter a gossip phase and exchange forwarded messages for third-party recipients using the existing Noise-encrypted channel.</p> 755 756 <h3>Third-Party Services</h3> 757 <p><strong>None.</strong> Operates entirely over BLE with no network connectivity.</p> 758 759 <h3>Security Properties</h3> 760 <ul> 761 <li><strong>Double encrypted:</strong> Application-level encryption (§3) + Noise session from BLE handshake</li> 762 <li><strong>Relay cannot read:</strong> Forwarded messages are encrypted for a specific recipient's exchange key</li> 763 <li><strong>Forwarding limits:</strong> 50MB max store, 48-hour TTL, probabilistic acceptance via bloom filters</li> 764 <li><strong>No metadata leakage:</strong> Relay device cannot determine sender, recipient, or content of forwarded messages</li> 765 </ul> 766 767 <!-- 9. MATRIX --> 768 <h2 id="matrix">9. Comparative Security Matrix</h2> 769 770 <table> 771 <tr> 772 <th>Property</th><th>BLE</th><th>DHT</th><th>Tor P2P</th><th>iroh</th> 773 </tr> 774 <tr> 775 <td><strong>Confidentiality</strong></td> 776 <td>ChaCha20 (2 layers)</td> 777 <td>ChaCha20</td> 778 <td>ChaCha20 + Tor</td> 779 <td>ChaCha20 (2 layers)</td> 780 </tr> 781 <tr> 782 <td><strong>Authenticity</strong></td> 783 <td>Ed25519 + Noise XX</td> 784 <td>Ed25519 + BEP44</td> 785 <td>Ed25519 + identity exchange</td> 786 <td>Ed25519 + QUIC TLS</td> 787 </tr> 788 <tr> 789 <td><strong>Forward secrecy</strong></td> 790 <td>Session + per-message</td> 791 <td>Per-message</td> 792 <td>Per-message + per-circuit</td> 793 <td>Per-message + per-connection</td> 794 </tr> 795 <tr> 796 <td><strong>Anonymity</strong></td> 797 <td>Proximity only</td> 798 <td>IP visible</td> 799 <td>Strong (.onion)</td> 800 <td>IP visible to relay</td> 801 </tr> 802 <tr> 803 <td><strong>3rd-party dependency</strong></td> 804 <td>None</td> 805 <td>Public DHT</td> 806 <td>Tor network</td> 807 <td>n0 relay</td> 808 </tr> 809 <tr> 810 <td><strong>Offline capable</strong></td> 811 <td>Yes</td> 812 <td>No</td> 813 <td>No</td> 814 <td>No</td> 815 </tr> 816 <tr> 817 <td><strong>Replay protection</strong></td> 818 <td>Nonce + msg ID</td> 819 <td>ID + seq + TTL</td> 820 <td>ID + replay cache</td> 821 <td>Message ID</td> 822 </tr> 823 <tr> 824 <td><strong>Mutual auth</strong></td> 825 <td>Noise XX</td> 826 <td>N/A (async)</td> 827 <td>Identity key exchange</td> 828 <td>QUIC + identity mapping</td> 829 </tr> 830 </table> 831 832 <!-- 10. ATTACKS --> 833 <h2 id="attacks">10. Attack Surface Analysis</h2> 834 835 <h3>Scenario 1: Compromised Recipient Exchange Key</h3> 836 <p><strong>Impact:</strong> Attacker can decrypt any message encrypted to this key that they can intercept.</p> 837 <table> 838 <tr><th>Transport</th><th>Risk</th><th>Details</th></tr> 839 <tr><td>BLE</td><td class="severity-low">Low</td><td>Requires physical proximity to intercept</td></tr> 840 <tr><td>DHT</td><td class="severity-high">High</td><td>Messages persist on public DHT for 48 hours; attacker can fetch and decrypt</td></tr> 841 <tr><td>Tor P2P</td><td class="severity-medium">Medium</td><td>Must intercept in real-time; Tor anonymity makes interception difficult</td></tr> 842 <tr><td>iroh</td><td class="severity-medium">Medium</td><td>Must intercept in real-time via relay or network position</td></tr> 843 </table> 844 <p><strong>Mitigation:</strong> Forward secrecy protects messages already decrypted. Only messages currently in transit or stored on DHT are at risk.</p> 845 846 <h3>Scenario 2: Compromised Sender Identity Key</h3> 847 <p><strong>All transports:</strong> Cannot decrypt past messages (identity keys are for signing, not encryption). Can impersonate sender going forward until key is rotated.</p> 848 849 <h3>Scenario 3: Global Passive Adversary</h3> 850 <table> 851 <tr><th>Transport</th><th>Observable</th><th>Not Observable</th></tr> 852 <tr><td>BLE</td><td>Radio signals within ~10m</td><td>Content (encrypted)</td></tr> 853 <tr><td>DHT</td><td>IP addresses, slot patterns, timing</td><td>Content, sender identity</td></tr> 854 <tr><td>Tor P2P</td><td>Tor circuit existence</td><td>.onion addresses, content, identities</td></tr> 855 <tr><td>iroh</td><td>IPs, relay connections, timing</td><td>Content (double-encrypted)</td></tr> 856 </table> 857 858 <h3>Scenario 4: Compromised Infrastructure</h3> 859 <table> 860 <tr><th>Infrastructure</th><th>Attacker Capability</th></tr> 861 <tr><td>DHT nodes</td><td>Store/serve ciphertext; cannot decrypt. Can refuse to store (DoS).</td></tr> 862 <tr><td>Tor relays</td><td>Route encrypted circuits; cannot decrypt. Guard node knows client IP.</td></tr> 863 <tr><td>n0 relay</td><td>See connection metadata and encrypted QUIC packets; cannot decrypt application-layer encryption.</td></tr> 864 <tr><td>BLE</td><td>N/A — no infrastructure</td></tr> 865 </table> 866 867 <h3>Scenario 5: Nonce Reuse</h3> 868 <p>ChaCha20-Poly1305 is catastrophically broken by nonce reuse with the same key. Ekko mitigates this through:</p> 869 <ol> 870 <li><strong>Random 96-bit nonces</strong> generated per message via OS CSPRNG</li> 871 <li><strong>Unique encryption keys</strong> per message (HKDF with message_id salt ensures different key even if nonce collides)</li> 872 <li><strong>Noise transport</strong> uses counter-based nonces (guaranteed unique within session)</li> 873 </ol> 874 <p><strong>Residual risk:</strong> Negligible. Birthday bound for 96-bit random nonces is 2<sup>48</sup> messages per key, and keys are never reused.</p> 875 876 <hr> 877 <p style="font-size: 9pt; color: #999; margin-top: 2em;"> 878 <em>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.</em> 879 </p> 880 881 </body> 882 </html>