# How BLEAnnounceHandlingPolicy Prevents Identity Spoofing in BitChat

> Discover how BitChat's BLEAnnounceHandlingPolicy prevents identity spoofing. Learn about its three-stage validation pipeline that cryptographically binds peer identity to signing keys.

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

---

**BitChat's BLEAnnounceHandlingPolicy prevents identity spoofing through a three-stage validation pipeline that cryptographically binds a peer's Noise identity to their Ed25519 signing key, rejecting any packet with mismatched, missing, or invalid signatures.**

BitChat is an open-source, peer-to-peer messenger that uses Bluetooth Low Energy (BLE) for device discovery. Because BLE operates in a trustless environment where any nearby device can broadcast announces, the protocol must cryptographically verify identity claims before trusting them. The `BLEAnnounceHandlingPolicy` in the permissionlesstech/bitchat repository implements this defense through layered checks in `BLEAnnouncePreflightPolicy`, `BLEAnnounceTrustPolicy`, and `BLEAnnounceResponsePolicy`.

## The Three-Stage Defense Against Identity Spoofing

The announce handling pipeline processes every incoming BLE packet through sequential validation stages. Each stage eliminates specific attack vectors, ensuring only cryptographically verified identities reach the application layer.

### Stage 1: Pre-Flight Validation (BLEAnnouncePreflightPolicy)

Before any cryptographic verification, `BLEAnnouncePreflightPolicy.evaluate` filters malformed, replayed, or self-targeted packets. This function, implemented in [`bitchat/Services/BLE/BLEAnnounceHandlingPolicy.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLEAnnounceHandlingPolicy.swift), performs four critical checks:

- **Decoding validation** – The packet must deserialize as a valid `AnnouncementPacket` (lines 28–30)
- **PeerID binding** – The derived `PeerID` from the announced Noise public key must match the BLE link-level `peerID` (lines 32–35)
- **Self-announce rejection** – The sender cannot be the local device (lines 37–39)
- **Freshness verification** – `BLEPacketFreshnessPolicy` rejects stale or replayed packets (lines 41–46)

Any mismatch aborts processing immediately, preventing unnecessary cryptographic operations on garbage data.

```swift
let preflight = BLEAnnouncePreflightPolicy.evaluate(
    packet: incomingPacket,
    from: remotePeerID,
    localPeerID: localPeerID,
    now: Date()
)

guard case .accept(let acceptance) = preflight else {
    // Rejected: malformed, stale, self-announce, or sender mismatch
    return
}

```

### Stage 2: Trust Verification (BLEAnnounceTrustPolicy)

The trust stage enforces the cryptographic binding between a peer's Noise identity and their Ed25519 signing key. `BLEAnnounceTrustPolicy.evaluate` implements four layered checks that directly prevent identity spoofing:

**Key mismatch rejection** – If the local database already knows a Noise public key for this peer, a different announced key triggers rejection (lines 82–84).

**Authenticated signing-key pinning** – When an authenticated Noise session exists, the associated Ed25519 signing key is pinned; any different announced signing key is rejected (lines 89–92).

**TOFU signing-key enforcement** – For previously observed peers, new signing keys are rejected, preventing silent key rotation attacks (lines 100–103).

**Signature validation** – Every packet must contain a valid Ed25519 signature; missing or invalid signatures cause immediate rejection (lines 105–111).

```swift
let trust = BLEAnnounceTrustPolicy.evaluate(
    hasSignature: incomingPacket.hasSignature,
    signatureValid: incomingPacket.signatureIsValid,
    existingNoisePublicKey: knownNoiseKey,
    announcedNoisePublicKey: acceptance.announcement.noisePublicKey,
    existingSigningPublicKey: knownSigningKey,
    authenticatedSigningPublicKey: session?.authenticatedSigningKey,
    announcedSigningPublicKey: acceptance.announcement.signingPublicKey
)

guard trust.isVerified else {
    // Rejected: missing/invalid signature, key mismatch, or signing-key mismatch
    return
}

```

This layered design prevents the core spoofing attack: an attacker cannot replay a victim's Noise public key while substituting their own signing key. Any deviation from previously bound keys is explicitly rejected.

### Stage 3: Response Planning (BLEAnnounceResponsePolicy)

Only packets passing both previous stages reach `BLEAnnounceResponsePolicy.plan`. This stage determines application-level responses—UI notifications, sync scheduling, or reciprocal announces—operating on a now-trusted identity (lines 31–38).

```swift
let response = BLEAnnounceResponsePolicy.plan(
    isDirectAnnounce: incomingPacket.isDirect,
    isNewPeer: !knownPeers.contains(acceptance.derivedPeerID),
    isReconnectedPeer: knownPeers.contains(acceptance.derivedPeerID),
    shouldSendAnnounceBack: true
)

if response.shouldNotifyPeerConnected { /* Update UI */ }
if response.shouldScheduleInitialSync { /* Start sync */ }
if response.shouldSendAnnounceBack   { /* Send reciprocal announce */ }

```

## How the Architecture Thwarts Specific Attacks

| Attack Vector | Defense Mechanism | Implementation Location |
|-------------|------------------|------------------------|
| Replay of old announces | `BLEPacketFreshnessPolicy` timestamp validation | `BLEAnnouncePreflightPolicy` lines 41–46 |
| Noise public key spoofing | PeerID derived from Noise key must match link-level ID | `BLEAnnouncePreflightPolicy` lines 32–35 |
| Signing key substitution | TOFU enforcement and authenticated session pinning | `BLEAnnounceTrustPolicy` lines 89–103 |
| Unsigned announces | Mandatory signature presence and validation | `BLEAnnounceTrustPolicy` lines 105–111 |
| Announce flooding | `BLEAnnounceThrottle` rate limiting | [`BLEAnnounceThrottle.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEAnnounceThrottle.swift) |

## Integration with the BLE Stack

The policy pipeline is orchestrated by [`BLEAnnounceHandler.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEAnnounceHandler.swift), which instantiates the three policies and executes them for each received packet. [`BLEService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEService.swift) sets up this handler and integrates throttling via [`BLEAnnounceThrottle.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEAnnounceThrottle.swift), adding rate-limiting to prevent flood-based attacks that might be used to probe for key acceptance windows.

According to the bitchat source code, this design follows defense-in-depth principles: cryptographic verification occurs only after structural validation, and all state mutations require explicit trust verification.

## Summary

- **BLEAnnounceHandlingPolicy** implements a three-stage pipeline: pre-flight validation, trust verification, and response planning.
- **Pre-flight checks** eliminate malformed, stale, and misrouted packets before cryptographic operations.
- **Trust verification** cryptographically binds Noise identities to Ed25519 signing keys, rejecting any substitution attempt.
- **TOFU and authenticated pinning** prevent silent key rotation and man-in-the-middle attacks during session establishment.
- **Signature enforcement** ensures every identity claim is backed by valid Ed25519 cryptography.

## Frequently Asked Questions

### What happens if an attacker replays a valid announce packet from a legitimate peer?

The `BLEPacketFreshnessPolicy` validates timestamps within `BLEAnnouncePreflightPolicy.evaluate` (lines 41–46). Stale packets are rejected, preventing replay attacks even if the signature would otherwise validate.

### Can a peer rotate their signing key if their device is compromised?

No automatic rotation is permitted. The TOFU (trust-on-first-use) model in `BLEAnnounceTrustPolicy` rejects new signing keys for previously known peers (lines 100–103). Key changes require out-of-band re-verification.

### How does BitChat handle the first contact with a new peer?

On first contact, the announced Noise public key and Ed25519 signing key are recorded as the trusted binding. Subsequent contact must present identical keys, enforced by the key mismatch checks in `BLEAnnounceTrustPolicy.evaluate` (lines 82–84).

### Where does the actual signature verification occur?

`BLEAnnounceTrustPolicy.evaluate` accepts pre-computed `signatureValid` and `hasSignature` parameters (lines 105–111). The cryptographic verification itself is performed by the caller before policy evaluation, typically using the Ed25519 implementation in the Noise protocol layer.