# How BitChat Implements Forward Secrecy for Live Sessions

> Discover how BitChat implements forward secrecy for live sessions using Noise XX handshake protocol and automatic re-keying. Protect your past communications now.

- Repository: [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)
- Tags: internals
- Published: 2026-07-29

---

**BitChat ensures forward secrecy by combining the Noise XX handshake protocol with automatic session re-keying every 60 seconds and one-time pre-key bundles for offline messages, ensuring compromised keys cannot decrypt past communications.**

The open-source BitChat project ([permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)) implements robust **forward secrecy** protections that prevent attackers from decrypting historical messages even if long-term identity keys are compromised later. This cryptographic guarantee is enforced through a layered approach spanning live interactive sessions and asynchronous store-and-forward messaging.

## Noise XX Handshake for Ephemeral Session Keys

At the foundation of BitChat's forward secrecy lies the **Noise XX handshake pattern**. When two peers establish a live session, the system initiates an ephemeral Diffie-Hellman exchange that generates fresh session keys never stored long-term.

In [`bitchat/Services/NoiseEncryptionService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/NoiseEncryptionService.swift), the `initiateHandshake` method creates a `NoiseSessionManager` instance with a static identity key, then executes the XX handshake through `processHandshakeMessage`. This design ensures that compromising a device's static identity key does **not** reveal traffic from past sessions, as the ephemeral keys used for derivation are destroyed immediately after the handshake completes.

## Automatic Session Re-keying

To limit the window of exposure for any single session key, BitChat implements **automatic re-keying** that forces periodic handshakes during active conversations.

The service initializes a `rekeyTimer` (line ≈ 58) that fires every `rekeyCheckInterval` (default 60 seconds). On each tick, `checkSessionsForRekey` (line ≈ 70) identifies sessions exceeding their configured lifetime and triggers `initiateAutomaticRekey` (line ≈ 81). This method orchestrates a fresh XX handshake via `onRekeyHandshakeReady`, replacing the old session key while the conversation remains active. This continuous rotation ensures that even if a session key is later exposed, it only protects a minimal time window of traffic.

## One-Time Pre-Key Bundles for Offline Security

For store-and-forward scenarios where peers are offline, BitChat maintains forward secrecy through **one-time pre-key bundles**. Each device gossips a batch of signed, single-use Curve25519 public keys. When sending to an offline peer, the sender calls `sealPrekeyPayload` (lines ≈ 92‑100) to encrypt the payload using the Noise X pattern, where the recipient's one-time pre-key acts as the static responder key.

After the recipient successfully decrypts the message via `openPrekeyPayload`, the pre-key is immediately marked as consumed and its private component permanently discarded (or automatically expired after a 48‑hour grace period). This mechanism guarantees that even a future compromise of the recipient's static identity key cannot decrypt already-consumed pre-key envelopes.

## Non-Forward-Secure Fallbacks

The codebase explicitly distinguishes between forward-secret and non-forward-secret transport methods. The `sealCourierPayload` and `openCourierPayload` methods (lines ≈ 44‑46) implement a static-key X pattern for one-way messages when a peer is reachable but no interactive session exists. According to the source code, these courier envelopes are **not** forward-secret, and the service prefers established sessions or pre-key envelopes whenever possible.

## Implementation Flow for Live Sessions

The complete forward secrecy workflow operates as follows:

1. **Session Creation** – `initiateHandshake` establishes a Noise XX session via `NoiseSessionManager`, deriving symmetric keys from ephemeral DH exchanges.
2. **Periodic Rotation** – The `rekeyTimer` triggers `checkSessionsForRekey` every minute, calling `initiateAutomaticRekey` to force fresh handshakes via `onRekeyHandshakeReady`.
3. **Active Encryption** – While sessions are valid, `encrypt(_:for:)` and `decrypt(_:from:)` use the current session key. If re-keying is required, the service emits `onHandshakeRequired` to signal the UI.
4. **Offline Handling** – For unreachable peers, `currentPrekeyBundle` retrieves fresh pre-keys, and `sealPrekeyPayload` creates forward-secret envelopes that are consumed permanently upon first decryption.

## Code Examples

```swift
// 1. Start a live session (XX handshake)
let handshakeData = try noiseService.initiateHandshake(with: peerID)
// → sends handshakeData over BLE; remote calls processHandshakeMessage

// 2. Send a message using current session key
let encrypted = try noiseService.encrypt(messageData, for: peerID)

// 3. Automatic re-key triggers after ~60 seconds via rekeyTimer
// UI listens to onRekeyHandshakeReady to transmit new handshake

// 4. Send offline message with forward secrecy
let prekeyBundle = try noiseService.currentPrekeyBundle()!
let chosenPrekey = prekeyBundle.prekeys.randomElement()!
let sealed = try noiseService.sealPrekeyPayload(payload, recipientPrekey: chosenPrekey)
// → Recipient calls openPrekeyPayload once, then key is destroyed

```

## Key Source Files

| File | Purpose |
|------|---------|
| [`bitchat/Services/NoiseEncryptionService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/NoiseEncryptionService.swift) | Core service managing Noise sessions, automatic re-keying, and envelope sealing |
| [`localPackages/BitFoundation/Sources/BitFoundation/PrekeyBundle.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/PrekeyBundle.swift) | Definition and signing of one-time pre-key bundles |
| [`localPackages/BitFoundation/Sources/BitFoundation/NoiseSessionManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/NoiseSessionManager.swift) | Low-level Noise protocol handling and session lifecycle |

## Summary

- BitChat uses the **Noise XX handshake** to derive ephemeral session keys that provide forward secrecy for live conversations.
- **Automatic re-keying** every 60 seconds limits the exposure window of any single session key through the `rekeyTimer` and `initiateAutomaticRekey` mechanisms.
- **One-time pre-key bundles** enable forward secrecy for offline messages via `sealPrekeyPayload`, with keys permanently deleted after first use or after 48 hours.
- Courier envelopes (`sealCourierPayload`) provide a fallback but explicitly sacrifice forward secrecy for connectivity.

## Frequently Asked Questions

### What is forward secrecy and why does BitChat use it?

Forward secrecy is a cryptographic property that ensures session keys are not compromised even if long-term private keys are exposed in the future. BitChat implements this to protect historical messages from retroactive decryption, ensuring that a stolen device or leaked identity key cannot be used to read past conversations.

### How often does BitChat rotate session keys?

By default, BitChat checks for sessions needing rotation every 60 seconds via the `rekeyCheckInterval` timer. The `checkSessionsForRekey` method evaluates session lifetimes and triggers `initiateAutomaticRekey` to force a fresh Noise XX handshake when thresholds are exceeded.

### What happens if a BitChat session key is compromised?

Due to automatic re-keying and the ephemeral nature of Noise XX handshakes, a compromised session key only exposes traffic from that specific short time window. Previous and subsequent sessions use independently derived keys that remain secure, as the ephemeral Diffie-Hellman exchange never persists long-term key material.

### How does BitChat handle forward secrecy for offline messages?

When peers are offline, BitChat uses one-time pre-key bundles defined in [`PrekeyBundle.swift`](https://github.com/permissionlesstech/bitchat/blob/main/PrekeyBundle.swift). The sender encrypts messages via `sealPrekeyPayload` using the Noise X pattern, and the recipient's private pre-key is destroyed immediately after `openPrekeyPayload` decrypts the message. This ensures offline messages have the same forward secrecy guarantees as live sessions.