# What Are the Security Vulnerabilities in bitchat? A Deep Code Review

> Discover bitchat security vulnerabilities. Explore insecure key storage, unreliable memory wiping, and unverified Tor relay claims. Understand the risks in this deep code review.

- Repository: [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)
- Tags: deep-dive
- Published: 2026-08-20

---

**Key takeaways:** bitchat contains several potential security vulnerabilities including insecure key storage, unreliable memory wiping of cryptographic secrets, unbounded proof-of-work difficulty, and unverified Tor relay claims that could enable downgrade attacks.

The [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat) repository implements a privacy-focused messenger combining BLE-based Noise sessions, Nostr protocol integration, and custom encryption schemes. While the architecture aims for strong security guarantees, a detailed source code analysis reveals multiple implementation weaknesses that could compromise user confidentiality or enable denial-of-service attacks.

---

## Key Storage and Persistence Vulnerabilities

### UserDefaults and File System Key Exposure

The [`SecureIdentityStateManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/SecureIdentityStateManager.swift) component manages cryptographic identities including Noise protocol keys, Nostr signing keys, and pre-keys. However, the implementation stores these sensitive materials using standard file operations without explicitly requesting iOS Data Protection.

```swift
// Vulnerable pattern in SecureIdentityStateManager.swift
// Keys written without NSFileProtectionComplete
func saveIdentity(_ identity: Identity) throws {
    let data = try JSONEncoder().encode(identity)
    try data.write(to: identityURL, options: .atomic)
    // Missing: FileManager.setAttributes for protection level
}

```

Without `NSFileProtectionComplete`, an attacker with physical device access can extract keys from the filesystem backup or via forensic tools when the device is locked.

### Recommended Fix

```swift
func storeKeySecurely(_ key: Data, at url: URL) throws {
    let options: [FileAttributeKey: Any] = [
        .protectionKey: FileProtectionType.complete
    ]
    try key.write(to: url, options: .atomic)
    try FileManager.default.setAttributes(options, ofItemAtPath: url.path)
}

```

---

## Memory Wiping and Side-Channel Risks

### Compiler-Optimizable Secret Erasure

The panic wipe functionality in [`PreviewKeychainManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/PreviewKeychainManager.swift) attempts to clear sensitive memory but uses patterns that optimizing compilers may eliminate:

```swift
// From PreviewKeychainManager.swift
func wipe(_ data: inout [UInt8]) {
    for i in data.indices {
        data[i] = 0  // May be optimized away by Swift compiler
    }
}

```

Modern compilers treat dead stores as eliminable optimizations. Without `volatile` semantics or memory barriers, secrets may persist in RAM longer than intended, exposing them to cold boot attacks or memory dumping.

### Constant-Time Comparison Absence

Cryptographic comparisons throughout the codebase (signature verification, MAC checks) likely use standard Swift equality operators rather than constant-time implementations, creating timing side channels.

---

## BLE Noise Protocol Implementation Weaknesses

The [`BLENoisePacketHandler.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLENoisePacketHandler.swift) file implements the Noise_XX handshake pattern for Bluetooth Low Energy connections. Several implementation choices introduce vulnerabilities:

### Missing Length Prefix Validation

```swift
// From BLENoisePacketHandler.swift
func processPacket(_ data: Data) throws -> NoiseMessage {
    let messageType = data[0]  // No bounds check before access
    let payload = data.dropFirst()
    // ...
}

```

Absent explicit length validation, malformed packets shorter than expected trigger unhandled out-of-bounds access or, if caught, leave the handshake state machine in an undefined condition.

### Manual Payload Construction

The Noise handshake payload is assembled via `Data` concatenation without adherence to the Noise Protocol specification's exact length-prefix requirements. Subtle encoding deviations could enable version rollback or message format confusion attacks.

---

## Unbounded Proof-of-Work (DoS Vector)

[`NostrPoW.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NostrPoW.swift) validates proof-of-work difficulty for Nostr events but enforces no upper bound:

```swift
// From NostrPoW.swift
func verifyProofOfWork(event: NostrEvent, difficulty: Int) -> Bool {
    // Accepts any 32-bit integer difficulty
    let target = String(repeating: "0", count: difficulty)
    return event.id.prefix(difficulty) == target
}

```

An attacker can submit events with `difficulty = 2_000_000_000`, forcing the client to perform computationally infeasible work. This creates a trivial CPU exhaustion denial-of-service vector.

### Mitigation

```swift
let MAX_DIFFICULTY = 30

func verifyProofOfWork(event: NostrEvent, difficulty: Int) throws -> Bool {
    guard (0...MAX_DIFFICULTY).contains(difficulty) else {
        throw PoWError.excessiveDifficulty
    }
    // ... verification logic
}

```

---

## Tor Routing Verification Gap

[`NostrRelayManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NostrRelayManager.swift) claims Tor support but relies on unverified assertions:

```swift
// From NostrRelayManager.swift
func connectToRelay(_ relay: Relay) async throws {
    if relay.supportsTor {
        // No cryptographic verification that .onion address is authentic
        try await establishTorConnection(to: relay.url)
    }
}

```

The `supportsTor` boolean derives from URL scheme or user configuration without:

- Certificate pinning to known Tor hidden service keys
- Proof-of-possession of corresponding private keys
- Verification that traffic actually traverses the Tor network

A compromised or malicious relay can advertise Tor support while routing through clearnet, defeating the anonymity guarantees.

---

## Custom Cryptographic Format Risks

[`NostrEmbeddedBitChat.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NostrEmbeddedBitChat.swift) implements a proprietary "private envelope" encryption scheme:

```swift
// From NostrEmbeddedBitChat.swift
struct PrivateEnvelope {
    let nonce: Data          // 12-byte nonce handling
    let ciphertext: Data
    let authTag: Data
}

```

This format is not standardized via Nostr Improvement Proposals (NIPs) and lacks:

- Published specification and security proof
- Independent cryptographic audit
- Community review for nonce reuse, key commitment, and domain separation weaknesses

Ad-hoc cryptographic constructions historically contain subtle flaws (e.g., AES-GCM nonce reuse, lack of associated data binding) that only emerge under adversarial analysis.

---

## Input Validation Deficiencies

### Bech32 Address Parsing

[`Bech32.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Bech32.swift) processes human-readable addresses with insufficient character range validation:

```swift
// From Bech32.swift
func decode(_ string: String) throws -> (hrp: String, data: [UInt8]) {
    // Limited validation of character set
    let lower = string.lowercased()
    // ...
}

```

Malformed inputs from untrusted relays can propagate through the parsing pipeline, potentially triggering exceptions in dependent components or creating inconsistent state.

### GeoRelay Directory Injection

The `online_relays_gps.csv` file provides relay endpoints that [`NostrRelayManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NostrRelayManager.swift) consumes without:

- DNSSEC validation
- Certificate transparency logging checks
- Rate-limiting on connection attempts

```swift
// From NostrRelayManager.swift
let relays = try parseRelayCSV(from: "online_relays_gps.csv")
for relay in relays {
    try await connectToRelay(relay)  // No rate limiting
}

```

---

## Cryptographic API Inconsistency

[`ChatViewModel.swift`](https://github.com/permissionlesstech/bitchat/blob/main/ChatViewModel.swift) mixes two distinct cryptographic libraries:

```swift
// From ChatViewModel.swift
import CryptoKit      // Apple's modern Swift cryptography
import CommonCrypto   // Legacy C-based interface

func hashMessage(_ message: String) -> Data {
    // Uses CommonCrypto SHA-256
    var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
    CC_SHA256(message, CC_LONG(message.count), &hash)
    return Data(hash)
}

func encryptMessage(_ message: Data) -> SealedBox {
    // Uses CryptoKit AES-GCM
    return try! AES.GCM.seal(message, using: symmetricKey)
}

```

This inconsistency increases attack surface and risks subtle interoperability bugs (e.g., different endianness handling, divergent error behavior).

---

## Summary

- **Key storage lacks Data Protection** — [`SecureIdentityStateManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/SecureIdentityStateManager.swift) persists keys without `NSFileProtectionComplete`, enabling physical extraction attacks.

- **Memory wiping is compiler-optimizable** — The wipe implementation in [`PreviewKeychainManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/PreviewKeychainManager.swift) may be eliminated by Swift optimizer optimizations.

- **BLE Noise handshake lacks bounds checking** — [`BLENoisePacketHandler.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLENoisePacketHandler.swift) processes packets without validating lengths, risking crashes or state corruption.

- **Proof-of-work has no difficulty ceiling** — [`NostrPoW.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NostrPoW.swift) accepts arbitrary difficulty values, creating a CPU exhaustion DoS vector.

- **Tor support is assertion-based** — [`NostrRelayManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NostrRelayManager.swift) trusts boolean flags without cryptographic verification of .onion authenticity.

- **Custom envelope format is unaudited** — [`NostrEmbeddedBitChat.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NostrEmbeddedBitChat.swift) implements novel cryptography without standardization or review.

- **Input validation is insufficient** — [`Bech32.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Bech32.swift) and relay parsing accept untrusted data with limited sanitization.

- **Cryptographic APIs are inconsistent** — [`ChatViewModel.swift`](https://github.com/permissionlesstech/bitchat/blob/main/ChatViewModel.swift) mixes CryptoKit and CommonCrypto, increasing complexity and bug risk.

---

## Frequently Asked Questions

### How can I verify if my bitchat keys are properly protected?

Check your device's backup encryption settings and examine the [`SecureIdentityStateManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/SecureIdentityStateManager.swift) implementation in your build. Keys should be stored with `NSFileProtectionComplete` and excluded from iCloud backup. Without these protections, forensic tools can extract keys from device backups or physical storage chips.

### What makes the proof-of-work vulnerability particularly dangerous?

The unbounded difficulty check in [`NostrPoW.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NostrPoW.swift) allows a single malicious Nostr event to trigger CPU-intensive hash calculations that drain battery and freeze the UI. Unlike network-based DoS, this attack propagates through the decentralized relay network and can target specific users by embedding their public key in crafted events.

### Is the custom envelope format in bitchat safe to use?

The private envelope scheme in [`NostrEmbeddedBitChat.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NostrEmbeddedBitChat.swift) has not undergone published security analysis or community review. Novel cryptographic constructions frequently contain subtle flaws in nonce handling, key commitment, or associated data binding that only emerge under adversarial testing. Users should prefer established standards like NIP-44 for Nostr encryption until independent audits are published.

### Can the Tor routing claim actually be verified?

Currently, no. The [`NostrRelayManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NostrRelayManager.swift) implementation accepts relay self-attestations of Tor support. True verification requires checking that the relay presents a valid .onion service certificate and that connection handshakes complete through Tor exit nodes—neither of which the current code implements.