blue_ghost logo

blue_ghost

system design

Architecture Overview

blue_ghost is built as a layered, offline‑first secure communication system. Each layer is isolated, minimal, and designed to eliminate entire classes of attack.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   Application UI                    β”‚
β”‚              (Jetpack Compose, MVVM)                β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   Session Manager    β”‚   Secure Input / Display     β”‚
β”‚   (BleManager.kt)    β”‚   (Password-mode keyboard,   β”‚
β”‚                      β”‚    FLAG_SECURE screenshots)  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚             Cryptographic Protocol Layer            β”‚
β”‚   Ephemeral ECDH Β· Double Ratchet Β· AES-256-GCM     β”‚
β”‚   HKDF-SHA256 Β· Replay Window Β· Safety Number       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚              Bluetooth Low Energy Transport         β”‚
β”‚          (GATT Server + Client, BLE Advertiser)     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚            Android Keystore (TEE-backed)            β”‚
β”‚         P-256 identity key β€” never extracted        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Component Breakdown

ECDH Handshake Flow

Device A (initiator)                    Device B (responder)
────────────────────                    ────────────────────
Generate EKa (ephemeral)
Send EKa.public ──────────────────────▢ Generate EKb (ephemeral)
                                        shared = ECDH(EKb.priv, EKa.pub)
                                        Derive via HKDF-SHA256 β†’
                                          rootKey       (32 bytes)
                                          recvChainKey  (32 bytes)
                                          sendChainKey  (32 bytes)
                ◀────────────────────── Send EKb.public
shared = ECDH(EKa.priv, EKb.pub)
Derive via HKDF-SHA256 β†’
  rootKey       (32 bytes)
  sendChainKey  (32 bytes)
  recvChainKey  (32 bytes)

Both sides now hold identical ratchet initialisation state.
No private material was ever transmitted.

Double Ratchet Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Root Key (RK)  β”‚  ← initialised from HKDF handshake output
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚  (advances on each DH ratchet step)
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   DH Ratchet     β”‚  ← new ephemeral keypair generated per turn
β”‚ (break-in recov) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Chain Key (CK)  β”‚  ← HMAC-SHA256 ratchet, one step per message
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Message Key (MK) β”‚  ← used once for AES-256-GCM, then wiped
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Each message uses a unique MK that is deleted immediately after use. The DH ratchet step re-derives RK and CK from a new ECDH output, ensuring that a future key compromise cannot retroactively expose past messages.

BLE Transport Model

blue_ghost uses BLE GATT characteristics as an encrypted bidirectional pipe. The advertiser broadcasts a minimal service UUID with avatar seed and busy-status bytes. Descriptions and names are fetched via GATT read on first contact.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   BLE Advertisement              β”‚
β”‚   Service UUID + avatar seed     β”‚
β”‚   + busy flag (5 bytes total)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚ connect
               β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   GATT Service: blue_ghost       			      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  NAME_CHAR    (read)                        β”‚
β”‚  DESC_CHAR    (read)                        β”‚
β”‚  SHAKE_CHAR   (write) ← handshake + control β”‚
β”‚  MESSAGE_CHAR (write) ← encrypted payloads  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

All message payloads are uniform-length encrypted blobs. No plaintext, metadata, or identifiers are ever transmitted in the clear.

Session Lifecycle

[ Set name + description ]
         β”‚
         β–Ό
[ Start scanning + advertising ]
         β”‚
         β–Ό
[ Peer discovered β†’ tap to view profile ]
         β”‚  GATT probe reads NAME + DESC
         β–Ό
[ Connect dialog β†’ send SHAKE ]
         β”‚  SHAKE payload: ephemeral pub + avatar + name + desc
         β–Ό
[ Peer accepts β†’ ACCEPT payload returned ]
         β”‚  ACCEPT payload: ephemeral pub + avatar + name + desc
         β–Ό
[ HKDF derives root + chain keys ]
         β”‚
         β–Ό
[ Double Ratchet active β€” encrypted messaging ]
         β”‚
         β–Ό
[ Session end β†’ Seal Ceremony ]
         β”‚
         β–Ό
[ All keys + history destroyed ]