/ docs / transport-security.html
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 &mdash; March 2026<br>
143      Technical Reference &mdash; 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 &#8594; iroh &#8594; Tor P2P &#8594; 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 &#215; 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 &#8741; 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] &#183; ephemeral_pub [32B]</text>
339    <text x="170" y="445" text-anchor="middle" font-size="8" fill="#ccc">nonce [12B] &#183; ciphertext [var + 16B tag]</text>
340    <text x="170" y="458" text-anchor="middle" font-size="8" fill="#ccc">timestamp [8B] &#183; 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 &#8741; 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 &#215; 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 &#8594; 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 &#8594; 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 (&sect;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: &#8594; 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: &#8592; 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: &#8594; 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> (&sect;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, &micro;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">(&sect;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 &#183; 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">(&sect;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" &#8741; recipient_exchange_public)[0..32] &nbsp; &#8594; &nbsp; 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 (&sect;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&ndash;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 &mdash; 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 (&sect;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" &#183; value=.onion address &#183; 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 (&sect;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 (&sect;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 &amp; 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 &mdash; 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 (&sect;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 (&sect;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 &mdash; 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 &mdash; 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>