# Security Implications of Broadcasting Public Keys and Nicknames in Cleartext on the BitChat Mesh

> Discover the security implications of broadcasting public keys and nicknames in cleartext on the BitChat mesh. Learn how this exposes device fingerprints and social graphs to passive tracking.

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

---

**Broadcasting public keys and nicknames in cleartext on the BitChat mesh creates permanent device fingerprints, exposes social graphs through neighbour lists, and enables passive tracking by anyone within BLE radio range.**

BitChat is an open-source, offline-first mesh messaging protocol that relies on Bluetooth Low Energy (BLE) broadcasts for peer discovery. In the current v1 implementation, devices transmit their Noise static public keys, Ed25519 signing keys, and user-selected nicknames in unencrypted *announce* packets every 4–30 seconds. This design fundamentally undermines user privacy by allowing passive adversaries to harvest persistent identifiers and reconstruct social relationships without ever joining the mesh.

## How BitChat V1 Broadcasts Data in Cleartext

BitChat’s legacy announce mechanism transmits discovery data using Type-Length-Value (TLV) encoding defined in [`localPackages/BitFoundation/Sources/BitFoundation/MessageType.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/MessageType.swift). The v1 packet structure exposes the following fields to any device within radio range:

| TLV | Field | Size | Risk |
|---|---|---|---|
| `0x01` | Nickname | Variable | Direct identity disclosure |
| `0x02` | Noise static public key | 32 bytes | Permanent device fingerprint |
| `0x03` | Ed25519 signing public key | 32 bytes | Authentication material leakage |
| `0x04` | Direct neighbour list | Up to 80 bytes | Social graph exposure |
| `0x05` | Capabilities | 1–8 bytes | Device capability profiling |

Because these packets are broadcast in cleartext, no authentication or encryption is required to capture them. As noted in [`docs/privacy-assessment.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/privacy-assessment.md), anyone with a standard BLE dongle can harvest this data silently, making large-scale surveillance trivially inexpensive.

## Critical Security Risks of Cleartext Broadcasting

### Permanent Device Fingerprinting via Static Noise Keys

The 32-byte Noise static public key (`0x02`) serves as a long-term cryptographic identity. BitChat derives the peer ID from this key using `SHA-256(noiseStaticPublicKey)[0..8]`, creating an immutable 64-bit identifier. Because the static key never rotates in v1, an adversary can track the device across all future encounters by simply hashing the observed public key. This defeats the purpose of peer ID rotation, which is designed to break linkability between sessions.

### Nickname Exposure and Identity Correlation

The `0x01` TLV transmits the user’s chosen nickname (e.g., "Alice") in plain UTF-8. Since nicknames are often reused across sessions, an observer can correlate the static public key with human-readable identifiers, building a persistent profile of the individual. In activist or protest settings where anonymity is critical, this correlation removes the deniability that rotating keys would otherwise provide.

### Social Graph Leakage Through Neighbour Lists

The neighbour list field (`0x04`) contains up to ten peer IDs representing the device's immediate mesh neighbours. By collecting multiple announces over time, an adversary can reconstruct physical adjacency relationships and map local social graphs without any active participation in the protocol. This metadata reveals who was physically close to whom and when, creating a movement and association fingerprint.

### Unauthenticated Key Binding and Replay Attacks

According to the source in [`localPackages/BitFoundation/Sources/BitFoundation/AnnounceV2Packet.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/AnnounceV2Packet.swift), legacy v1 announce packets are **unsigned** ("Unsigned, on purpose and not without cost"). Because the public keys are transmitted in cleartext without authentication, an attacker can replay a victim’s static Noise and Ed25519 keys while substituting their own signing material. While BitChat uses TOFU (trust-on-first-use) pinning to mitigate this, a sophisticated adversary who intercepts the initial handshake can impersonate victims by replaying their static identity data.

## The Path Forward: Announce V2 Privacy Architecture

To eliminate these vulnerabilities, the BitChat team developed `AnnounceV2Packet`, defined in [`localPackages/BitFoundation/Sources/BitFoundation/AnnounceV2Packet.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/AnnounceV2Packet.swift). This format deliberately omits the cleartext fields that cause linkability:

- **No static public keys**: The Noise and Ed25519 keys are removed from the broadcast entirely.
- **No nickname**: Human-readable identifiers are transmitted only inside the encrypted Noise XX handshake.
- **Epoch-based rotation**: V2 transmits only an epoch number, a 16-byte tag block, optional capabilities, and an optional bridge geohash.

Section 4.5 of [`docs/PEER-ID-ROTATION.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/PEER-ID-ROTATION.md) proposes binding the rotating peer ID to the static keys using a signed proof inside the encrypted handshake, preventing unauthenticated replay attacks. Additionally, the protocol will use **pairwise recognition tags**—cryptographically derived values that only mutual favorites can compute—eliminating social-graph leakage while maintaining fast reconnection capabilities.

## Practical Code Examples

The following Swift snippets demonstrate the difference between the legacy v1 implementation (which exposes cleartext identity material) and the privacy-preserving v2 format.

### Decoding a Legacy V1 Announce (Cleartext Exposure)

```swift
import BitFoundation

// Simulate raw BLE packet captured from air
let rawData: Data = /* BLE advertisement payload */

if let announce = AnnouncementPacket.decode(from: rawData) {
    // All fields are extracted in cleartext
    print("Nickname:        \(announce.nicknameString)")
    print("Noise Public Key: \(announce.noisePublicKey.hex)")
    print("Signing Key:     \(announce.signingPublicKey.hex)")
    
    // Calculate permanent peer ID
    let peerId = SHA256.hash(data: announce.noisePublicKey).prefix(8)
    print("Derived Peer ID: \(peerId.hex)")
}

```

### Encoding a Privacy-Preserving V2 Announce

```swift
import BitFoundation

// V2 contains no public keys or nicknames
let currentEpoch: UInt32 = 42
let tagBlock = Data(repeating: 0, count: AnnounceV2Packet.tagBlockLength)

let v2Packet = AnnounceV2Packet(
    epoch: currentEpoch,
    tagBlock: tagBlock,
    capabilities: nil,
    bridgeGeohash: nil
)

if let encoded = v2Packet.encode() {
    // Broadcast over BLE - contains only epoch and opaque tags
    blePeripheralManager.updateValue(encoded, ...)
}

```

## Summary

- **Legacy v1 announces** transmit Noise static public keys, Ed25519 signing keys, nicknames, and neighbour lists in cleartext every 4–30 seconds, as defined in [`MessageType.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MessageType.swift).
- **Permanent tracking** is possible because the peer ID is derived directly from the unchanging static public key (`SHA-256(noiseStaticPublicKey)[0..8]`).
- **Social graph analysis** is trivial due to the neighbour list TLV (`0x04`) which reveals immediate adjacency relationships.
- **Replay attacks** are possible because v1 packets are unsigned, allowing adversaries to rebroadcast captured static keys.
- **Announce V2** mitigates these issues by removing all cleartext identity material, using only epoch numbers and pairwise recognition tags, as implemented in [`AnnounceV2Packet.swift`](https://github.com/permissionlesstech/bitchat/blob/main/AnnounceV2Packet.swift).

## Frequently Asked Questions

### What specific data is exposed when BitChat broadcasts public keys and nicknames in cleartext?

BitChat’s v1 announce packets expose the user’s nickname (TLV `0x01`), a 32-byte Noise static public key (TLV `0x02`), a 32-byte Ed25519 signing key (TLV `0x03`), and a list of up to ten neighbour peer IDs (TLV `0x04`). All of this data is transmitted in cleartext over BLE, allowing any nearby device to harvest identity material without authentication.

### How does broadcasting static public keys defeat BitChat’s peer ID rotation mechanism?

BitChat derives the peer ID from the static Noise public key using `SHA-256(noiseStaticPublicKey)[0..8]`. Since the static key is broadcast in cleartext and never changes, an adversary can calculate the peer ID at any time, linking the device across all rotation epochs. According to [`docs/PEER-ID-ROTATION.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/PEER-ID-ROTATION.md) section 4.2, rotation only provides privacy if the rotating ID is derived from private key material; broadcasting the public key allows observers to compute all past and future IDs.

### Can attackers replay captured BitChat announce packets to impersonate users?

Yes. The legacy v1 announce packets are unsigned (as noted in [`AnnounceV2Packet.swift`](https://github.com/permissionlesstech/bitchat/blob/main/AnnounceV2Packet.swift)), meaning there is no cryptographic proof of origin. An attacker can capture a victim’s static Noise and Ed25519 keys and rebroadcast them with their own data. While BitChat uses TOFU (trust-on-first-use) pinning to detect changes, a sophisticated adversary who intercepts the initial handshake can exploit this to impersonate the victim before the legitimate device connects.

### What mitigations does BitChat implement to stop cleartext broadcasting of public keys?

BitChat is implementing **Announce V2**, which removes all cleartext identity fields from the broadcast. Instead of public keys and nicknames, V2 transmits only an epoch counter, a 16-byte opaque tag block, and optional capabilities. The static keys and nicknames are moved inside the encrypted Noise XX handshake, and [`docs/PEER-ID-ROTATION.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/PEER-ID-ROTATION.md) proposes signed bindings to prevent replay attacks while maintaining the ability for mutual favorites to recognize each other using pairwise-derived tags.