# How BitChat Implements Noise Protocol Encryption for Live Sessions

> Discover how BitChat uses Noise protocol encryption for secure live sessions. Learn about its handshake pattern, key management, and stateful channel implementation for enhanced privacy.

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

---

**BitChat secures live sessions using the Noise XX handshake pattern with Curve25519 static identity keys and Ed25519 signing keys, implementing a stateful, forward-secret channel through automatic re-keying and per-peer session management.**

The open-source BitChat application (permissionlesstech/bitchat) provides end-to-end encryption for peer-to-peer messaging by leveraging the Noise protocol framework. Its Swift-based implementation centers on the Noise XX pattern for mutual authentication and forward secrecy, managed through a centralized encryption service that handles identity persistence, handshake negotiation, and automatic session rotation.

## Five-Stage Encryption Architecture

BitChat's encryption implementation follows a structured lifecycle defined in [`NoiseEncryptionService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NoiseEncryptionService.swift). The architecture progresses through five distinct stages to establish and maintain secure communications.

### Stage 1: Identity Provision with Curve25519 and Ed25519

On service initialization, BitChat loads or generates persistent cryptographic identities. The service retrieves a **Curve25519 static key** and an **Ed25519 signing key** from the iOS Keychain, creating the long-term identity used across all Noise handshakes.

According to the source code in [`bitchat/Services/NoiseEncryptionService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/NoiseEncryptionService.swift), the static key handling occurs at lines 45-71, while the signing-key logic appears at lines 88-104. These keys provide the `s` (static) parameter required by the Noise XX pattern, enabling mutual authentication during session establishment.

### Stage 2: Per-Peer Session Management

The `NoiseSessionManager` (instantiated at lines 31-33) maintains a pool of per-peer Noise sessions. This manager tracks session state, generation UUIDs, and re-key timers for each connected peer.

Session creation happens lazily when a handshake is requested. The manager wires callback handlers at lines 44-55 to delegate handshake completion events back to the encryption service. This architecture isolates session state management from the high-level encryption API.

### Stage 3: Noise XX Handshake Negotiation

The handshake flow implements the Noise XX pattern (and X variant for one-way envelopes) through three distinct phases:

**Initiation:** The `initiateHandshake(with:)` method (lines 99-119) constructs raw Noise handshake packets by delegating to `sessionManager.initiateHandshake`. This generates ephemeral keys and creates the initial handshake message for transmission over BLE.

**Inbound Processing:** The `processHandshakeMessageWithResult` method (lines 108-128) validates peer IDs, message sizes, and rate limits before forwarding to `handleIncomingHandshakeWithResult` on the session manager. This validation prevents resource exhaustion attacks during handshake negotiation.

**Completion:** When handshakes succeed, `handleSessionEstablished` (lines 223-242) records the peer's SHA-256 fingerprint and fires authentication handlers, transitioning the session to the transport phase where encrypted messaging can begin.

### Stage 4: Authenticated Message Encryption

Once sessions are established, BitChat provides symmetric encryption through dedicated methods:

**Encryption:** The `encrypt(_:for:)` method (lines 68-88) validates message sizes, checks rate limits, and verifies session existence before calling `sessionManager.encrypt`. This produces authenticated ciphertext ready for transmission.

**Decryption:** The `decrypt(_:from:)` method (and its detailed variant `decryptWithSessionGeneration`) retrieves the appropriate receive session and performs authenticated decryption while invoking rate-limiting checks (lines 126-136). Failed decryption attempts trigger security throttling to prevent oracle attacks.

### Stage 5: Automatic Re-keying and Rotation

BitChat implements forward secrecy through automatic session rotation. A periodic `rekeyTimer` (started at lines 558-562) executes `checkSessionsForRekey` (lines 570-579) to identify sessions requiring rotation.

When rotation is needed, the service calls `initiateAutomaticRekey` (lines 581-586) to perform a new handshake without user intervention. This re-keying mechanism ensures that compromise of a single session key does not compromise historical message traffic.

## Core Implementation Files

The encryption stack spans multiple modules within the repository:

- **[`bitchat/Services/NoiseEncryptionService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/NoiseEncryptionService.swift)**: High-level API coordinating identity keys, handshake lifecycles, and encryption primitives. This file contains the main service initialization, handshake initiation (lines 99-119), and encryption routines (lines 68-88).

- **[`localPackages/BitFoundation/Sources/BitFoundation/NoiseSessionManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/NoiseSessionManager.swift)**: Internal session state machine managing per-peer Noise protocol states, handshake processing, and re-key scheduling.

- **[`localPackages/BitFoundation/Sources/BitFoundation/NoiseHandshakeState.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/NoiseHandshakeState.swift)**: Low-level wrapper around the Noise library implementing XX and X pattern handshake message construction and parsing.

- **[`localPackages/BitFoundation/Sources/BitFoundation/PeerID.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/PeerID.swift)**: Utility functions converting between Noise public keys and BitChat's 16-character hexadecimal routing IDs used for peer identification.

## Practical Integration Example

The following Swift code demonstrates the complete lifecycle of establishing an encrypted session:

```swift
import BitFoundation

// 1. Initialize the service with Keychain persistence
let noiseService = NoiseEncryptionService(keychain: MyKeychainManager())

// 2. Initiate handshake with a 16-hex peer ID
let handshakeData = try noiseService.initiateHandshake(with: peerID)
// Transmit handshakeData over BLE...

// 3. Process remote handshake response
let reply = try noiseService.processHandshakeMessage(
    from: peerID,
    message: incomingData)
// Transmit reply back over BLE...

// 4. Encrypt payload once session is established
let plaintext = "Hello, world!".data(using: .utf8)!
let encrypted = try noiseService.encrypt(plaintext, for: peerID)
// Transmit encrypted payload...

// 5. Decrypt incoming messages
let receivedPlaintext = try noiseService.decrypt(incomingCiphertext, from: peerID)

```

This implementation provides **mutual authentication** through static identity keys and **forward secrecy** through per-session ephemeral keys generated by the Noise XX pattern.

## Summary

BitChat's encryption architecture delivers enterprise-grade security through the Noise protocol:

- **Noise XX Pattern**: Provides mutual authentication and forward secrecy via ephemeral key exchanges authenticated by static Curve25519 identities.
- **Hierarchical Key Management**: Separates long-term identity keys (Ed25519 and Curve25519) from session ephemerals, stored securely in the iOS Keychain.
- **Automatic Rotation**: The `rekeyTimer` and `checkSessionsForRekey` mechanism enforce cryptographic hygiene without user intervention.
- **Rate-Limited Operations**: All encryption and handshake operations include throttling to prevent denial-of-service attacks.
- **Modular Design**: The separation between [`NoiseEncryptionService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NoiseEncryptionService.swift) and `NoiseSessionManager` enables testing and protocol updates.

## Frequently Asked Questions

### What Noise handshake pattern does BitChat use for live sessions?

BitChat implements the **Noise XX** pattern for bidirectional live sessions, which provides mutual authentication and forward secrecy. For one-way envelopes, it utilizes the **Noise X** variant. These patterns are implemented in [`NoiseHandshakeState.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NoiseHandshakeState.swift) and invoked through the session manager.

### How does BitChat handle identity verification during encryption setup?

BitChat verifies identity through **Ed25519 signing keys** and **Curve25519 static keys** loaded from the iOS Keychain during service initialization (lines 45-71 and 88-104 of [`NoiseEncryptionService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NoiseEncryptionService.swift)). When a session is established, `handleSessionEstablished` (lines 223-242) records the peer's SHA-256 fingerprint, enabling persistent identity validation across sessions.

### What happens when a BitChat encryption session expires?

When a session approaches expiration, the `rekeyTimer` triggers `checkSessionsForRekey` (lines 570-579) to identify stale sessions. The service then automatically initiates a re-key handshake via `initiateAutomaticRekey` (lines 581-586), establishing fresh ephemeral keys without interrupting the user experience or requiring manual key exchange.

### Where does BitChat store cryptographic keys for encryption?

BitChat stores long-term **Curve25519 static keys** and **Ed25519 signing keys** in the **iOS Keychain**, accessed through the injected keychain manager during `NoiseEncryptionService` initialization. Per-session ephemeral keys reside in memory within `NoiseSessionManager` and are never persisted to disk, ensuring forward secrecy.