How BitChat Implements Noise Protocol Encryption for Live Sessions

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. 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, 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:

Practical Integration Example

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

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 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 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). 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →