/ dead-drop-protocol.md
dead-drop-protocol.md
  1  # Dead Drop Protocol: Direct Delivery Model
  2  
  3  ## Overview
  4  
  5  This document specifies the state machines and protocol flows for a proximity-based asynchronous messaging system. The core premise: messages are composed at any time but only delivered when sender and recipient devices come into physical proximity.
  6  
  7  ---
  8  
  9  ## 1. Cryptographic Primitives
 10  
 11  ### Key Types
 12  
 13  ```
 14  Identity Key Pair (long-term)
 15  ├── id_private: Ed25519 private key (signing)
 16  └── id_public:  Ed25519 public key (verification)
 17  
 18  Exchange Key Pair (long-term)  
 19  ├── ex_private: X25519 private key (key agreement)
 20  └── ex_public:  X25519 public key (shared with contacts)
 21  
 22  Ephemeral Key Pair (per-message)
 23  ├── eph_private: X25519 private key (one-time use)
 24  └── eph_public:  X25519 public key (sent with message)
 25  ```
 26  
 27  ### Cryptographic Functions
 28  
 29  ```
 30  ECDH(private, public) → shared_secret
 31  HKDF(secret, salt, info) → derived_key
 32  ChaCha20-Poly1305(key, nonce, plaintext) → ciphertext + tag
 33  Ed25519_Sign(private, message) → signature
 34  Ed25519_Verify(public, message, signature) → bool
 35  BLAKE2b(data) → hash
 36  ```
 37  
 38  ---
 39  
 40  ## 2. Device States
 41  
 42  ### 2.1 Global Device State Machine
 43  
 44  ```
 45  ┌─────────────────────────────────────────────────────────────┐
 46  │                      DEVICE STATES                          │
 47  ├─────────────────────────────────────────────────────────────┤
 48  │                                                             │
 49  │    ┌──────────┐                                             │
 50  │    │   OFF    │ ◄─────── App closed / BLE disabled          │
 51  │    └────┬─────┘                                             │
 52  │         │                                                   │
 53  │         │ App launched + BLE enabled                        │
 54  │         ▼                                                   │
 55  │    ┌──────────┐                                             │
 56  │    │  IDLE    │ ◄─────┐                                     │
 57  │    └────┬─────┘       │                                     │
 58  │         │             │                                     │
 59  │         │ Periodic    │ Scan complete                       │
 60  │         │ scan timer  │                                     │
 61  │         ▼             │                                     │
 62  │    ┌──────────┐       │                                     │
 63  │    │ SCANNING │ ──────┘                                     │
 64  │    └────┬─────┘                                             │
 65  │         │                                                   │
 66  │         │ Peer discovered                                   │
 67  │         ▼                                                   │
 68  │    ┌──────────┐                                             │
 69  │    │HANDSHAKE │ ──────► Success ──────► EXCHANGING          │
 70  │    └────┬─────┘                                             │
 71  │         │                                                   │
 72  │         │ Timeout/Failure                                   │
 73  │         ▼                                                   │
 74  │    Back to IDLE                                             │
 75  │                                                             │
 76  └─────────────────────────────────────────────────────────────┘
 77  ```
 78  
 79  ### 2.2 State Descriptions
 80  
 81  | State | Description | BLE Activity |
 82  |-------|-------------|--------------|
 83  | OFF | App not running or BLE disabled | None |
 84  | IDLE | Awaiting next scan interval | Advertising only |
 85  | SCANNING | Actively scanning for peers | Advertising + Scanning |
 86  | HANDSHAKE | Establishing secure channel with peer | Connected, Noise handshake |
 87  | EXCHANGING | Transferring messages | Connected, data transfer |
 88  
 89  ---
 90  
 91  ## 3. Contact Relationship States
 92  
 93  ### 3.1 Contact State Machine
 94  
 95  ```
 96  ┌─────────────────────────────────────────────────────────────┐
 97  │                    CONTACT STATES                           │
 98  ├─────────────────────────────────────────────────────────────┤
 99  │                                                             │
100  │    ┌──────────────┐                                         │
101  │    │   UNKNOWN    │  No relationship exists                 │
102  │    └──────┬───────┘                                         │
103  │           │                                                 │
104  │           │ QR code scanned (in-person exchange)            │
105  │           ▼                                                 │
106  │    ┌──────────────┐                                         │
107  │    │  PENDING     │  Keys exchanged, awaiting confirmation  │
108  │    └──────┬───────┘                                         │
109  │           │                                                 │
110  │           │ Confirmation handshake completed                │
111  │           ▼                                                 │
112  │    ┌──────────────┐                                         │
113  │    │   ACTIVE     │  Can send/receive messages              │
114  │    └──────┬───────┘                                         │
115  │           │                                                 │
116  │           │ User deletes contact                            │
117  │           ▼                                                 │
118  │    ┌──────────────┐                                         │
119  │    │   REVOKED    │  Relationship terminated                │
120  │    └──────────────┘                                         │
121  │                                                             │
122  └─────────────────────────────────────────────────────────────┘
123  ```
124  
125  ### 3.2 Contact Data Structure
126  
127  ```
128  Contact {
129      contact_id:         [u8; 16]      // Random identifier
130      nickname:           String        // User-assigned label
131      their_id_public:    Ed25519Pub    // Their identity key
132      their_ex_public:    X25519Pub     // Their exchange key
133      state:              ContactState  // PENDING | ACTIVE | REVOKED
134      created_at:         Timestamp     // When relationship established
135      last_seen:          Timestamp     // Last proximity event
136      last_delivery:      Timestamp     // Last successful message exchange
137  }
138  ```
139  
140  ---
141  
142  ## 4. Message States
143  
144  ### 4.1 Outbound Message State Machine
145  
146  ```
147  ┌─────────────────────────────────────────────────────────────┐
148  │                 OUTBOUND MESSAGE STATES                     │
149  ├─────────────────────────────────────────────────────────────┤
150  │                                                             │
151  │    ┌──────────────┐                                         │
152  │    │   DRAFT      │  User composing message                 │
153  │    └──────┬───────┘                                         │
154  │           │                                                 │
155  │           │ User confirms send                              │
156  │           ▼                                                 │
157  │    ┌──────────────┐                                         │
158  │    │   QUEUED     │  Encrypted, awaiting proximity          │
159  │    └──────┬───────┘                                         │
160  │           │                                                 │
161  │           │ Recipient in proximity, transmission started    │
162  │           ▼                                                 │
163  │    ┌──────────────┐                                         │
164  │    │ TRANSMITTING │  Active BLE transfer                    │
165  │    └──────┬───────┘                                         │
166  │           │                                                 │
167  │           ├── Success ──────────────┐                       │
168  │           │                         ▼                       │
169  │           │                  ┌──────────────┐               │
170  │           │                  │  DELIVERED   │               │
171  │           │                  └──────┬───────┘               │
172  │           │                         │                       │
173  │           │                         │ Recipient sends ACK   │
174  │           │                         ▼                       │
175  │           │                  ┌──────────────┐               │
176  │           │                  │ ACKNOWLEDGED │               │
177  │           │                  └──────────────┘               │
178  │           │                                                 │
179  │           └── Failure (timeout, disconnect)                 │
180  │                         │                                   │
181  │                         ▼                                   │
182  │                  Back to QUEUED                             │
183  │                                                             │
184  │    ┌──────────────┐                                         │
185  │    │   EXPIRED    │  TTL exceeded without delivery          │
186  │    └──────────────┘                                         │
187  │                                                             │
188  └─────────────────────────────────────────────────────────────┘
189  ```
190  
191  ### 4.2 Inbound Message State Machine
192  
193  ```
194  ┌─────────────────────────────────────────────────────────────┐
195  │                  INBOUND MESSAGE STATES                     │
196  ├─────────────────────────────────────────────────────────────┤
197  │                                                             │
198  │    ┌──────────────┐                                         │
199  │    │  RECEIVING   │  BLE transfer in progress               │
200  │    └──────┬───────┘                                         │
201  │           │                                                 │
202  │           │ Transfer complete, decryption successful        │
203  │           ▼                                                 │
204  │    ┌──────────────┐                                         │
205  │    │   RECEIVED   │  Stored locally, unread                 │
206  │    └──────┬───────┘                                         │
207  │           │                                                 │
208  │           │ User opens message                              │
209  │           ▼                                                 │
210  │    ┌──────────────┐                                         │
211  │    │    READ      │  User has viewed content                │
212  │    └──────┬───────┘                                         │
213  │           │                                                 │
214  │           │ Auto-delete timer OR user deletes               │
215  │           ▼                                                 │
216  │    ┌──────────────┐                                         │
217  │    │   DELETED    │  Securely wiped from storage            │
218  │    └──────────────┘                                         │
219  │                                                             │
220  └─────────────────────────────────────────────────────────────┘
221  ```
222  
223  ### 4.3 Message Data Structure
224  
225  ```
226  OutboundMessage {
227      message_id:         [u8; 16]          // Random unique ID
228      recipient_id:       [u8; 16]          // Contact ID
229      content_type:       ContentType       // TEXT | DOCUMENT
230      plaintext:          Vec<u8>           // Original content
231      ciphertext:         Vec<u8>           // Encrypted blob
232      ephemeral_public:   X25519Pub         // One-time key for this message
233      state:              OutboundState     // Current state
234      created_at:         Timestamp         // When composed
235      expires_at:         Timestamp         // TTL deadline
236      attempts:           u32               // Delivery attempt count
237  }
238  
239  InboundMessage {
240      message_id:         [u8; 16]          // From sender
241      sender_id:          [u8; 16]          // Contact ID
242      content_type:       ContentType       // TEXT | DOCUMENT
243      plaintext:          Vec<u8>           // Decrypted content
244      state:              InboundState      // Current state
245      received_at:        Timestamp         // When received
246      read_at:            Option<Timestamp> // When opened
247      delete_at:          Timestamp         // Auto-delete deadline
248  }
249  ```
250  
251  ---
252  
253  ## 5. Protocol Flows
254  
255  ### 5.1 Contact Establishment (One-Time Setup)
256  
257  This happens once, in person, to establish the relationship.
258  
259  ```
260  ┌─────────┐                                      ┌─────────┐
261  │  Alice  │                                      │   Bob   │
262  └────┬────┘                                      └────┬────┘
263       │                                                │
264       │  ┌─────────────────────────────────────────┐   │
265       │  │  PHYSICAL MEETING (same location)       │   │
266       │  └─────────────────────────────────────────┘   │
267       │                                                │
268       │  1. Alice displays QR code                     │
269       │  ┌──────────────────────────────────┐          │
270       │  │ QR contains:                     │          │
271       │  │   - Alice's id_public            │          │
272       │  │   - Alice's ex_public            │          │
273       │  │   - Random session_id            │          │
274       │  │   - Timestamp                    │          │
275       │  └──────────────────────────────────┘          │
276       │                                                │
277       │                      Bob scans QR              │
278       │ ◄──────────────────────────────────────────────│
279       │                                                │
280       │  2. Bob displays QR code                       │
281       │          ┌──────────────────────────────────┐  │
282       │          │ QR contains:                     │  │
283       │          │   - Bob's id_public              │  │
284       │          │   - Bob's ex_public              │  │
285       │          │   - Same session_id (proof)      │  │
286       │          │   - Timestamp                    │  │
287       │          └──────────────────────────────────┘  │
288       │                                                │
289       │  Alice scans QR                                │
290       │ ───────────────────────────────────────────────►
291       │                                                │
292       │  3. Both devices now have each other's keys    │
293       │                                                │
294       │  4. BLE confirmation handshake (optional)      │
295       │ ◄──────────────────────────────────────────────►
296       │     Noise_XX handshake to verify keys          │
297       │                                                │
298       │  5. Contact state → ACTIVE on both devices     │
299       │                                                │
300       ▼                                                ▼
301  ```
302  
303  ### 5.2 BLE Advertising & Discovery
304  
305  Devices continuously advertise their presence without revealing identity.
306  
307  ```
308  ┌─────────────────────────────────────────────────────────────┐
309  │                    BLE ADVERTISEMENT                        │
310  ├─────────────────────────────────────────────────────────────┤
311  │                                                             │
312  │  Advertisement Payload (31 bytes max):                      │
313  │  ┌─────────────────────────────────────────────────────┐    │
314  │  │ [2 bytes] Service UUID: 0xDD01 (Dead Drop)          │    │
315  │  │ [16 bytes] Rotating ID: HKDF(id_private, time_slot) │    │
316  │  │ [1 byte] Flags: has_messages | accepts_messages     │    │
317  │  │ [4 bytes] Time slot (15-minute granularity)         │    │
318  │  └─────────────────────────────────────────────────────┘    │
319  │                                                             │
320  │  Rotating ID changes every 15 minutes to prevent tracking.  │
321  │  Contacts can derive expected rotating IDs for each other.  │
322  │                                                             │
323  │  Detection:                                                 │
324  │  1. Scan for Service UUID 0xDD01                            │
325  │  2. For each contact, compute expected rotating ID          │
326  │  3. Compare against scanned rotating IDs                    │
327  │  4. Match found → contact is nearby                         │
328  │                                                             │
329  └─────────────────────────────────────────────────────────────┘
330  ```
331  
332  #### Rotating ID Calculation
333  
334  ```
335  time_slot = floor(unix_timestamp / 900)  // 15-minute slots
336  
337  rotating_id = HKDF(
338      secret: id_private,
339      salt:   time_slot.to_bytes(),
340      info:   "dead-drop-rotating-id-v1"
341  )[0..16]
342  ```
343  
344  Both parties can compute each other's rotating ID because they have exchanged public keys during setup, and derive a shared secret:
345  
346  ```
347  shared_secret = ECDH(my_ex_private, their_ex_public)
348  
349  expected_rotating_id = HKDF(
350      secret: shared_secret,
351      salt:   time_slot.to_bytes(),
352      info:   "dead-drop-rotating-id-v1"
353  )[0..16]
354  ```
355  
356  ### 5.3 Proximity Detection & Connection
357  
358  ```
359  ┌─────────┐                                      ┌─────────┐
360  │  Alice  │                                      │   Bob   │
361  │(sender) │                                      │(recipient)
362  └────┬────┘                                      └────┬────┘
363       │                                                │
364       │  ══════ Both devices advertising ══════       │
365       │                                                │
366       │  1. Alice's scan detects Bob's rotating ID    │
367       │     (Alice has queued messages for Bob)       │
368       │                                                │
369       │  2. Alice initiates BLE connection            │
370       │ ───────────────────────────────────────────────►
371       │           GATT Connect Request                 │
372       │                                                │
373       │  3. Connection established                     │
374       │ ◄───────────────────────────────────────────────
375       │           GATT Connect Response                │
376       │                                                │
377       │  4. Noise XX Handshake begins                  │
378       │                                                │
379       │  ┌─────────────────────────────────────────┐   │
380       │  │ Noise_XX_25519_ChaChaPoly_BLAKE2b       │   │
381       │  └─────────────────────────────────────────┘   │
382       │                                                │
383       │  → e                                           │
384       │ ───────────────────────────────────────────────►
385       │   Alice sends ephemeral public key             │
386       │                                                │
387       │  ← e, ee, s, es                                │
388       │ ◄───────────────────────────────────────────────
389       │   Bob sends ephemeral + static, encrypted      │
390       │                                                │
391       │  → s, se                                       │
392       │ ───────────────────────────────────────────────►
393       │   Alice sends static, encrypted                │
394       │                                                │
395       │  5. Handshake complete                         │
396       │     Both parties authenticated                 │
397       │     Transport keys established                 │
398       │                                                │
399       ▼                                                ▼
400  ```
401  
402  ### 5.4 Message Encryption (Pre-Delivery)
403  
404  Messages are encrypted immediately when sent, not when proximity is detected.
405  
406  ```
407  ┌─────────────────────────────────────────────────────────────┐
408  │                  MESSAGE ENCRYPTION                         │
409  ├─────────────────────────────────────────────────────────────┤
410  │                                                             │
411  │  Input:                                                     │
412  │    - plaintext: message content                             │
413  │    - recipient: Bob's contact record                        │
414  │                                                             │
415  │  Step 1: Generate ephemeral key pair                        │
416  │    eph_private, eph_public = X25519_Generate()              │
417  │                                                             │
418  │  Step 2: Derive shared secret                               │
419  │    shared = ECDH(eph_private, bob.ex_public)                │
420  │                                                             │
421  │  Step 3: Derive message key                                 │
422  │    message_key = HKDF(                                      │
423  │        secret: shared,                                      │
424  │        salt:   message_id,                                  │
425  │        info:   "dead-drop-message-v1"                       │
426  │    )                                                        │
427  │                                                             │
428  │  Step 4: Encrypt content                                    │
429  │    nonce = random(12)                                       │
430  │    ciphertext = ChaCha20Poly1305_Encrypt(                   │
431  │        key:   message_key,                                  │
432  │        nonce: nonce,                                        │
433  │        plaintext: plaintext,                                │
434  │        aad:   message_id || timestamp                       │
435  │    )                                                        │
436  │                                                             │
437  │  Step 5: Assemble encrypted message                         │
438  │    encrypted_message = {                                    │
439  │        message_id:       [16 bytes]                         │
440  │        ephemeral_public: eph_public [32 bytes]              │
441  │        nonce:            [12 bytes]                         │
442  │        ciphertext:       [variable]                         │
443  │        timestamp:        [8 bytes]                          │
444  │    }                                                        │
445  │                                                             │
446  │  Step 6: Sign the package                                   │
447  │    signature = Ed25519_Sign(                                │
448  │        alice.id_private,                                    │
449  │        BLAKE2b(encrypted_message)                           │
450  │    )                                                        │
451  │                                                             │
452  │  Output: encrypted_message || signature                     │
453  │                                                             │
454  └─────────────────────────────────────────────────────────────┘
455  ```
456  
457  ### 5.5 Message Exchange (Post-Handshake)
458  
459  ```
460  ┌─────────┐                                      ┌─────────┐
461  │  Alice  │                                      │   Bob   │
462  └────┬────┘                                      └────┬────┘
463       │                                                │
464       │  ══════ Noise session established ══════      │
465       │                                                │
466       │  1. Alice checks outbound queue for Bob       │
467       │     Found: 2 queued messages                  │
468       │                                                │
469       │  2. Send message count                        │
470       │ ───────────────────────────────────────────────►
471       │   MSG_COUNT { count: 2 }                       │
472       │                                                │
473       │  3. Bob acknowledges, sends his count         │
474       │ ◄───────────────────────────────────────────────
475       │   MSG_COUNT { count: 0 }                       │
476       │                                                │
477       │  4. Alice sends first message                 │
478       │ ───────────────────────────────────────────────►
479       │   MSG_DATA { index: 0, total: 2,              │
480       │              payload: encrypted_message_1 }   │
481       │                                                │
482       │  5. Bob acknowledges receipt                  │
483       │ ◄───────────────────────────────────────────────
484       │   MSG_ACK { index: 0, status: OK }            │
485       │                                                │
486       │  6. Alice sends second message                │
487       │ ───────────────────────────────────────────────►
488       │   MSG_DATA { index: 1, total: 2,              │
489       │              payload: encrypted_message_2 }   │
490       │                                                │
491       │  7. Bob acknowledges receipt                  │
492       │ ◄───────────────────────────────────────────────
493       │   MSG_ACK { index: 1, status: OK }            │
494       │                                                │
495       │  8. Exchange complete                         │
496       │ ───────────────────────────────────────────────►
497       │   SESSION_COMPLETE { }                         │
498       │                                                │
499       │  9. Disconnect                                │
500       │ ◄──────────────────────────────────────────────►
501       │   BLE Disconnect                               │
502       │                                                │
503       │  10. Alice marks messages DELIVERED           │
504       │      Bob decrypts and stores messages         │
505       │                                                │
506       ▼                                                ▼
507  ```
508  
509  ### 5.6 Message Decryption (Recipient Side)
510  
511  ```
512  ┌─────────────────────────────────────────────────────────────┐
513  │                  MESSAGE DECRYPTION                         │
514  ├─────────────────────────────────────────────────────────────┤
515  │                                                             │
516  │  Input:                                                     │
517  │    - encrypted_message || signature                         │
518  │    - sender: Alice's contact record                         │
519  │                                                             │
520  │  Step 1: Verify signature                                   │
521  │    valid = Ed25519_Verify(                                  │
522  │        alice.id_public,                                     │
523  │        BLAKE2b(encrypted_message),                          │
524  │        signature                                            │
525  │    )                                                        │
526  │    if (!valid) reject("Invalid signature")                  │
527  │                                                             │
528  │  Step 2: Extract components                                 │
529  │    message_id = encrypted_message[0..16]                    │
530  │    eph_public = encrypted_message[16..48]                   │
531  │    nonce = encrypted_message[48..60]                        │
532  │    ciphertext = encrypted_message[60..-8]                   │
533  │    timestamp = encrypted_message[-8..]                      │
534  │                                                             │
535  │  Step 3: Check for replay                                   │
536  │    if (message_id in seen_messages) reject("Replay")        │
537  │    if (timestamp too old) reject("Expired")                 │
538  │                                                             │
539  │  Step 4: Derive shared secret                               │
540  │    shared = ECDH(bob.ex_private, eph_public)                │
541  │                                                             │
542  │  Step 5: Derive message key                                 │
543  │    message_key = HKDF(                                      │
544  │        secret: shared,                                      │
545  │        salt:   message_id,                                  │
546  │        info:   "dead-drop-message-v1"                       │
547  │    )                                                        │
548  │                                                             │
549  │  Step 6: Decrypt content                                    │
550  │    plaintext = ChaCha20Poly1305_Decrypt(                    │
551  │        key:   message_key,                                  │
552  │        nonce: nonce,                                        │
553  │        ciphertext: ciphertext,                              │
554  │        aad:   message_id || timestamp                       │
555  │    )                                                        │
556  │                                                             │
557  │  Step 7: Store decrypted message                            │
558  │    seen_messages.add(message_id)  // Replay protection      │
559  │    store_message(plaintext)                                 │
560  │                                                             │
561  │  Output: plaintext                                          │
562  │                                                             │
563  └─────────────────────────────────────────────────────────────┘
564  ```
565  
566  ---
567  
568  ## 6. Wire Protocol
569  
570  ### 6.1 Message Types
571  
572  ```
573  ┌─────────────────────────────────────────────────────────────┐
574  │                    WIRE MESSAGE TYPES                       │
575  ├────────┬────────────────────────────────────────────────────┤
576  │ Type   │ Description                                        │
577  ├────────┼────────────────────────────────────────────────────┤
578  │ 0x01   │ MSG_COUNT    - Number of messages to exchange      │
579  │ 0x02   │ MSG_DATA     - Encrypted message payload           │
580  │ 0x03   │ MSG_ACK      - Acknowledge receipt of message      │
581  │ 0x04   │ MSG_NACK     - Negative acknowledgement (error)    │
582  │ 0x05   │ SESSION_DONE - Exchange complete, disconnect       │
583  │ 0x06   │ PING         - Keep-alive                          │
584  │ 0x07   │ PONG         - Keep-alive response                 │
585  └────────┴────────────────────────────────────────────────────┘
586  ```
587  
588  ### 6.2 Message Structures
589  
590  ```
591  All messages are sent inside Noise transport encryption.
592  
593  MSG_COUNT (0x01)
594  ┌──────────┬──────────────────┐
595  │ type: u8 │ count: u16       │
596  │ 0x01     │ number of msgs   │
597  └──────────┴──────────────────┘
598  
599  MSG_DATA (0x02)
600  ┌──────────┬──────────┬──────────┬─────────────────────┐
601  │ type: u8 │ index: u8│ total: u8│ payload: Vec<u8>    │
602  │ 0x02     │ 0-255    │ 0-255    │ encrypted message   │
603  └──────────┴──────────┴──────────┴─────────────────────┘
604  
605  MSG_ACK (0x03)
606  ┌──────────┬──────────┬──────────────────┐
607  │ type: u8 │ index: u8│ status: u8       │
608  │ 0x03     │ 0-255    │ 0=OK, 1+=error   │
609  └──────────┴──────────┴──────────────────┘
610  
611  MSG_NACK (0x04)
612  ┌──────────┬──────────┬──────────────────┐
613  │ type: u8 │ index: u8│ error_code: u8   │
614  │ 0x04     │ 0-255    │ see error table  │
615  └──────────┴──────────┴──────────────────┘
616  
617  SESSION_DONE (0x05)
618  ┌──────────┐
619  │ type: u8 │
620  │ 0x05     │
621  └──────────┘
622  
623  PING (0x06) / PONG (0x07)
624  ┌──────────┬──────────────────┐
625  │ type: u8 │ timestamp: u64   │
626  │ 0x06/07  │ unix millis      │
627  └──────────┴──────────────────┘
628  ```
629  
630  ### 6.3 Error Codes
631  
632  ```
633  ┌──────────┬────────────────────────────────────────┐
634  │ Code     │ Description                            │
635  ├──────────┼────────────────────────────────────────┤
636  │ 0x00     │ OK - Success                           │
637  │ 0x01     │ INVALID_SIGNATURE - Sig check failed   │
638  │ 0x02     │ DECRYPT_FAILED - Could not decrypt     │
639  │ 0x03     │ REPLAY_DETECTED - Message ID seen      │
640  │ 0x04     │ EXPIRED - Timestamp too old            │
641  │ 0x05     │ UNKNOWN_SENDER - Not in contacts       │
642  │ 0x06     │ STORAGE_FULL - Cannot accept more      │
643  │ 0x07     │ INVALID_FORMAT - Malformed message     │
644  └──────────┴────────────────────────────────────────┘
645  ```
646  
647  ---
648  
649  ## 7. Storage Schema
650  
651  ### 7.1 Local Database Structure
652  
653  ```sql
654  -- Identity (single row, device keys)
655  CREATE TABLE identity (
656      id              INTEGER PRIMARY KEY DEFAULT 1,
657      id_private      BLOB NOT NULL,      -- Ed25519 private (encrypted at rest)
658      id_public       BLOB NOT NULL,      -- Ed25519 public
659      ex_private      BLOB NOT NULL,      -- X25519 private (encrypted at rest)
660      ex_public       BLOB NOT NULL,      -- X25519 public
661      created_at      INTEGER NOT NULL    -- Unix timestamp
662  );
663  
664  -- Contacts
665  CREATE TABLE contacts (
666      contact_id      BLOB PRIMARY KEY,   -- 16 bytes random
667      nickname        TEXT,               -- User label
668      their_id_public BLOB NOT NULL,      -- Ed25519 public
669      their_ex_public BLOB NOT NULL,      -- X25519 public
670      state           INTEGER NOT NULL,   -- 0=PENDING, 1=ACTIVE, 2=REVOKED
671      created_at      INTEGER NOT NULL,
672      last_seen       INTEGER,            -- Last proximity
673      last_delivery   INTEGER             -- Last message exchange
674  );
675  
676  -- Outbound message queue
677  CREATE TABLE outbound_messages (
678      message_id      BLOB PRIMARY KEY,   -- 16 bytes random
679      recipient_id    BLOB NOT NULL,      -- FK to contacts
680      content_type    INTEGER NOT NULL,   -- 0=TEXT, 1=DOCUMENT
681      plaintext       BLOB NOT NULL,      -- Original (encrypted at rest)
682      ciphertext      BLOB NOT NULL,      -- Ready to transmit
683      eph_public      BLOB NOT NULL,      -- Ephemeral key used
684      state           INTEGER NOT NULL,   -- 0=QUEUED, 1=TRANSMITTING, etc
685      created_at      INTEGER NOT NULL,
686      expires_at      INTEGER NOT NULL,   -- TTL
687      attempts        INTEGER DEFAULT 0,
688      FOREIGN KEY (recipient_id) REFERENCES contacts(contact_id)
689  );
690  
691  -- Inbound messages
692  CREATE TABLE inbound_messages (
693      message_id      BLOB PRIMARY KEY,   -- From sender
694      sender_id       BLOB NOT NULL,      -- FK to contacts
695      content_type    INTEGER NOT NULL,
696      plaintext       BLOB NOT NULL,      -- Decrypted (encrypted at rest)
697      state           INTEGER NOT NULL,   -- 0=RECEIVED, 1=READ, 2=DELETED
698      received_at     INTEGER NOT NULL,
699      read_at         INTEGER,
700      delete_at       INTEGER NOT NULL,   -- Auto-delete deadline
701      FOREIGN KEY (sender_id) REFERENCES contacts(contact_id)
702  );
703  
704  -- Replay protection
705  CREATE TABLE seen_messages (
706      message_id      BLOB PRIMARY KEY,
707      seen_at         INTEGER NOT NULL
708  );
709  
710  -- Cleanup old replay records
711  CREATE INDEX idx_seen_messages_time ON seen_messages(seen_at);
712  ```
713  
714  ### 7.2 Encryption at Rest
715  
716  All sensitive fields (`plaintext`, private keys) are encrypted using a key derived from:
717  
718  ```
719  storage_key = HKDF(
720      secret: user_passphrase,  // Or biometric-protected key
721      salt:   device_random_salt,
722      info:   "dead-drop-storage-v1"
723  )
724  ```
725  
726  ---
727  
728  ## 8. Security Considerations
729  
730  ### 8.1 Threat Model
731  
732  **Protected against:**
733  - Passive network observers (no network traffic to observe)
734  - Server compromise (no servers)
735  - Metadata collection (no logs of who contacts whom)
736  - Message content exposure (end-to-end encrypted)
737  - Replay attacks (message IDs tracked)
738  - Device tracking via BLE (rotating IDs)
739  
740  **Not protected against:**
741  - Physical device seizure (mitigated by encryption at rest)
742  - Compromised endpoint (malware on device)
743  - Rubber hose cryptanalysis (coercion)
744  - Global passive adversary correlating physical movements
745  - RF fingerprinting of specific BLE hardware
746  
747  ### 8.2 Forward Secrecy
748  
749  Each message uses a fresh ephemeral key pair. Compromise of long-term keys does not reveal past messages (only future messages to that contact).
750  
751  For enhanced forward secrecy, implement key ratcheting:
752  
753  ```
754  After each successful message exchange:
755  1. Both parties derive new exchange keys
756  2. new_ex_secret = HKDF(old_ex_secret || shared_random)
757  3. Old keys are securely deleted
758  ```
759  
760  ### 8.3 Deniability
761  
762  The protocol provides limited deniability:
763  - Messages are signed, so sender cannot deny sending to the recipient
764  - However, signatures use identity keys known only to the two parties
765  - A recipient cannot prove to a third party that the sender sent a message
766    (recipient could have forged the signature using shared knowledge)
767  
768  For stronger deniability, consider ring signatures or designated verifier proofs.
769  
770  ### 8.4 Traffic Analysis Resistance
771  
772  To prevent observers from inferring communication patterns:
773  
774  1. **Constant-rate BLE activity** - Device advertises at fixed intervals regardless of message queue state
775  2. **Dummy connections** - Occasionally connect to random Dead Drop devices and exchange empty payloads
776  3. **Fixed message sizes** - Pad all messages to fixed sizes (e.g., 1KB, 4KB, 16KB buckets)
777  4. **Randomized timing** - Add jitter to all operations
778  
779  ---
780  
781  ## 9. Implementation Notes
782  
783  ### 9.1 BLE Considerations
784  
785  - MTU negotiation: Request maximum MTU (512 bytes on modern devices)
786  - Fragmentation: Messages larger than MTU must be fragmented at application layer
787  - Connection interval: Balance power consumption vs transfer speed
788  - Background operation: iOS and Android have different restrictions for background BLE
789  
790  ### 9.2 Platform-Specific Issues
791  
792  **iOS:**
793  - Background BLE advertising limited to specific service UUIDs
794  - Cannot scan in background unless looking for specific peripherals
795  - Use significant location changes or iBeacon regions to wake app
796  
797  **Android:**
798  - Requires location permission for BLE scanning (even without using location)
799  - Doze mode restricts background operation
800  - Use foreground service for reliable operation
801  
802  ### 9.3 Battery Optimization
803  
804  - Scan in short bursts (e.g., 5 seconds every 60 seconds)
805  - Increase scan frequency when motion detected (accelerometer)
806  - Decrease scan frequency when stationary for extended periods
807  - Allow user to configure aggressiveness vs battery tradeoff
808  
809  ---
810  
811  ## 10. Future Extensions
812  
813  ### 10.1 Relay Network
814  
815  Add ability for messages to hop through intermediate devices:
816  - Onion encryption for multi-hop routing
817  - Store-and-forward on relay devices
818  - Incentive mechanism for relay participation
819  
820  ### 10.2 Group Dead Drops
821  
822  Multiple parties sharing a dead drop:
823  - Shared group key derived from all members' keys
824  - Any member can deposit, any member can retrieve
825  - Useful for small cells or teams
826  
827  ### 10.3 Plausible Deniability Features
828  
829  - Hidden volumes (decoy contacts/messages with different passphrase)
830  - Duress PIN (wipes real data, shows decoy data)
831  - Steganographic storage (messages hidden in normal-looking files)
832  
833  ---
834  
835  ## Appendix A: Example Session Transcript
836  
837  ```
838  [Time: 0ms] Alice device scanning...
839  [Time: 50ms] Detected BLE advertisement: Service=0xDD01, RotatingID=a1b2c3...
840  [Time: 51ms] Computing expected rotating ID for contact "Bob"...
841  [Time: 52ms] Match found! Bob is nearby.
842  [Time: 53ms] Checking outbound queue... 1 message queued for Bob
843  [Time: 54ms] Initiating BLE connection to Bob's device
844  [Time: 150ms] GATT connection established
845  [Time: 151ms] Starting Noise_XX handshake
846  [Time: 152ms] → e (32 bytes)
847  [Time: 200ms] ← e, ee, s, es (96 bytes)
848  [Time: 201ms] → s, se (48 bytes)
849  [Time: 250ms] Handshake complete. Transport keys established.
850  [Time: 251ms] Sending MSG_COUNT { count: 1 }
851  [Time: 300ms] Received MSG_COUNT { count: 0 }
852  [Time: 301ms] Sending MSG_DATA { index: 0, total: 1, payload: [...] }
853  [Time: 450ms] Received MSG_ACK { index: 0, status: OK }
854  [Time: 451ms] Sending SESSION_DONE
855  [Time: 500ms] BLE disconnected
856  [Time: 501ms] Message state → DELIVERED
857  [Time: 502ms] Updating last_seen for contact "Bob"
858  ```
859  
860  ---
861  
862  ## Appendix B: Test Vectors
863  
864  ### B.1 Rotating ID Derivation
865  
866  ```
867  Input:
868    shared_secret: 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
869    time_slot: 1234567 (as u64 big-endian: 0x000000000012d687)
870  
871  Output:
872    rotating_id: 0x[first 16 bytes of HKDF output]
873  
874  Verification:
875    HKDF-SHA256(
876      IKM: shared_secret,
877      salt: time_slot_bytes,
878      info: "dead-drop-rotating-id-v1",
879      L: 16
880    )
881  ```
882  
883  ### B.2 Message Encryption
884  
885  ```
886  Input:
887    plaintext: "Hello, Bob!"
888    message_id: 0x00112233445566778899aabbccddeeff
889    sender_ex_private: [32 bytes]
890    recipient_ex_public: [32 bytes]
891  
892  Output:
893    encrypted_message: [structured as defined in 5.4]
894  ```
895  
896  (Actual test vectors would include concrete hex values for implementation testing)