blue_ghost logo

blue_ghost

protocol specification

Overview

A complete technical specification of the cryptographic layers, wire protocol, and session lifecycle. For a plain-English summary see Home; for privacy guarantees see Privacy.

Identity Layer β€” Hardware‑Backed Keys

Each device's long-term identity is a P-256 elliptic curve keypair generated inside Android Keystore and bound to the device's Trusted Execution Environment. The private key is non-exportable β€” it never touches the JVM heap, and cannot be extracted even if the OS is compromised.

Handshake Layer β€” Ephemeral ECDH

When two devices connect, they perform an ephemeral ECDH (P-256) key exchange to establish a shared secret without transmitting any private material. Both devices generate fresh ephemeral keypairs for each session β€” no long-term key material is reused across sessions.

Device A (initiator)                       Device B (responder)
──────────────────                         ────────────────────
Generate ephemeral keypair EKa             Generate ephemeral keypair EKb
Send EKa.public          ──────────────▢  Receive EKa.public
                                           Compute shared = ECDH(EKb.private, EKa.public)
                         ◀──────────────  Send EKb.public
Compute shared = ECDH(EKa.private, EKb.public)

Both sides derive:
  [rootKey β€– sendChainKey β€– recvChainKey] = HKDF-SHA256(shared, 96 bytes)

HKDF-SHA256 expands the raw shared secret into three 32-byte keys used to initialise the Double Ratchet. No static keys participate in the handshake, so there is no pre-key infrastructure to compromise.

Messaging Layer β€” Double Ratchet

Once the handshake completes, every message is encrypted with a unique key derived from two interleaved ratchets:

This provides:

Encryption Layer β€” AES-256-GCM

Each message key from the ratchet is used once for AES-256-GCM encryption:

Replay & Injection Protection

Every message carries a monotonically increasing sequence number that is authenticated into the GCM tag via AAD. The receiver maintains a 1024-message sliding window bitmap. Any message with a sequence number outside the window, or a number already seen within it, is rejected before decryption is attempted.

Peer Verification β€” Safety Number

To defend against MITM attacks during the handshake, both devices can compare a Safety Number β€” a human-readable fingerprint derived from both identity public keys. If the numbers match when read aloud or compared in person, the session is authentic.

The Safety Number is computed as SHA-256 over the lexicographically ordered concatenation of both identity public keys, formatted as 12 groups of 5 digits β€” the same approach used by Signal.

Secure Teardown β€” Seal Ceremony

When a session ends, all cryptographic session material is actively destroyed:

The long-term identity key in Android Keystore is never destroyed β€” it persists as your stable identity across sessions, but is never used for encryption.