# BitChat AuthenticatedPeerStatePacket Structure: A Complete Technical Guide to the TLV Wire Format

> Explore the AuthenticatedPeerStatePacket structure in BitChat. Understand its TLV wire format for exchanging peer capabilities and Ed25519 keys in Noise sessions.

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

---

**The `AuthenticatedPeerStatePacket` is a compact, versioned TLV (type-length-value) binary packet used in BitChat's Noise sessions to exchange peer capabilities and Ed25519 signing public keys, consisting of a 1-byte version (`0x01`), a capabilities TLV block, and a 32-byte signing key TLV block.**

BitChat implements peer authentication over Bluetooth Low Energy (BLE) using a specific binary protocol defined in the `permissionlesstech/bitchat` repository. Understanding the `AuthenticatedPeerStatePacket` structure is essential for developers building compatible clients or auditing the cryptographic handshake.

## Binary Wire Format Structure

The packet follows a strict canonical encoding designed for forward compatibility. According to the Swift implementation in [`bitchat/Protocols/Packets.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Protocols/Packets.swift) (lines 172-199), the wire format uses a fixed version byte followed by two TLV-encoded fields.

### Version Field

The first byte is always `0x01`, indicating the current wire format version. Decoding fails immediately if this byte does not match, preventing interpretation of future incompatible protocol versions.

### Capabilities TLV (Type 0x01)

Next comes the peer capabilities metadata:

- **TLV Type:** `0x01`
- **Content:** A `PeerCapabilities` object serialized via its `encoded()` method
- **Constraints:** Length-prefixed encoding with a maximum payload of 8 bytes
- **Purpose:** Advertises feature support such as private media streaming and group chat participation

Unknown capability bits are ignored during decoding to allow forward-compatible extensions.

### Signing Public Key TLV (Type 0x02)

The final mandatory component contains the cryptographic identity:

- **TLV Type:** `0x02`
- **Content:** 32-byte Ed25519 public key
- **Size:** Fixed at 32 bytes (`0x20` in hex)
- **Purpose:** Used to verify the peer's identity signatures during the Noise XX handshake

The complete byte layout appears as:

```

[0x01]                           // Version byte
[0x01][len][capability-bytes]    // Capabilities TLV (variable length ≤ 8)
[0x02][0x20][32-byte signing key] // Signing key TLV (fixed length)

```

## Packet Type Constants

Within the BitChat protocol hierarchy, this packet carries the type identifier `authenticatedPeerState = 0x21`. This constant is defined in [`bitchat/Protocols/BitchatProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Protocols/BitchatProtocol.swift), where it distinguishes authenticated state payloads from other Noise protocol messages like handshake initiations or data packets.

## Swift Implementation Details

The reference implementation resides in [`bitchat/Protocols/Packets.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Protocols/Packets.swift). The `AuthenticatedPeerStatePacket` struct provides two primary methods:

- `encode() -> Data?`: Validates the signing key length and capability size before producing the canonical TLV byte sequence
- `decode(from: Data) -> AuthenticatedPeerStatePacket?`: Performs strict validation including version checking, TLV ordering verification, and rejection of duplicate tags

Validation errors (malformed TLVs, non-canonical capability encoding, or incorrect key lengths) result in `nil` returns, ensuring that only well-formed data affects the session state.

## Working with AuthenticatedPeerStatePacket in Code

### Encoding an Outgoing Packet

To construct and serialize a packet for transmission over BLE:

```swift
import BitFoundation

// Configure capabilities to advertise
let capabilities = PeerCapabilities(
    supportsPrivateMedia: true,
    supportsGroupChat: true
)

// Obtain your 32-byte Ed25519 signing public key
let signingKey: Data = secureIdentityManager.signingPublicKey

// Construct the packet
let statePacket = AuthenticatedPeerStatePacket(
    capabilities: capabilities,
    signingPublicKey: signingKey
)

// Serialize for BLE transmission
guard let encoded = statePacket.encode() else {
    fatalError("Failed to encode AuthenticatedPeerStatePacket")
}
// encoded is now ready for the BLE Noise layer

```

The `encode()` method enforces all length constraints before returning the raw bytes.

### Decoding a Received Packet

When processing incoming data from the BLE Noise layer:

```swift
// payload is Data received from peer
if let packet = AuthenticatedPeerStatePacket.decode(from: payload) {
    let peerCapabilities = packet.capabilities
    let peerSigningKey = packet.signingPublicKey
    
    // Update session state with verified peer information
    sessionStore.updatePeerCapabilities(peerCapabilities, 
                                       signingKey: peerSigningKey)
} else {
    // Invalid version, malformed TLV, or invalid key length
    print("Discarding invalid AuthenticatedPeerStatePacket")
}

```

The decoder strictly rejects ambiguous or malformed input to prevent state corruption.

## Integration and Validation

The packet integrates into BitChat's transport layer through [`bitchat/Services/BLE/BLENoisePayloadFactory.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLENoisePayloadFactory.swift), which wraps the encoded bytes into typed BLE payloads. Upon receipt, [`bitchat/Services/BLE/BLEPrivateMediaSessionStore.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLEPrivateMediaSessionStore.swift) manages the lifecycle of authenticated peer states, persisting the verified capabilities and signing keys per session.

Security-critical validation rules include:
- **Version enforcement:** Only `0x01` is accepted
- **Canonical encoding:** Non-minimal TLV length encodings are rejected
- **Duplicate detection:** Repeated TLV tags cause immediate parsing failure
- **Key validation:** Signing keys must be exactly 32 bytes

Unit tests in [`bitchatTests/Protocols/PacketsTests.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/Protocols/PacketsTests.swift) verify these constraints, ensuring that forward-compatible extensions do not compromise security.

## Summary

- The `AuthenticatedPeerStatePacket` uses a **versioned TLV structure** with fixed version `0x01` for forward compatibility.
- It contains two TLV fields: **capabilities** (type `0x01`, max 8 bytes) and **Ed25519 signing public key** (type `0x02`, exactly 32 bytes).
- The packet type constant `0x21` identifies it within the Noise protocol framing.
- Implementation in [`bitchat/Protocols/Packets.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Protocols/Packets.swift) provides strict validation through `encode()` and `decode(from:)` methods.
- Unknown TLV types are safely ignored, while malformed packets are rejected to prevent session state corruption.

## Frequently Asked Questions

### What happens if a peer sends an AuthenticatedPeerStatePacket with an unknown version byte?

The `decode(from:)` method in [`bitchat/Protocols/Packets.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Protocols/Packets.swift) immediately returns `nil` if the version byte is not exactly `0x01`. This ensures that clients running older code do not misinterpret future protocol versions, maintaining backward compatibility through explicit version rejection rather than silent failure.

### Why are TLV length fields limited to specific sizes in the AuthenticatedPeerStatePacket?

The capabilities field is limited to 8 bytes to constrain the attack surface for denial-of-service via memory exhaustion, while the signing key requires exactly 32 bytes to match Ed25519 public key specifications. These constraints are enforced during both encoding and decoding operations in the Swift implementation.

### Can the AuthenticatedPeerStatePacket be extended with new fields without breaking existing clients?

Yes, the TLV design supports forward-compatible extensions. New fields can be added with unique type tags (values greater than `0x02`), and existing implementations will ignore unknown TLV types while continuing to process the required version, capabilities, and signing key fields. This allows gradual protocol upgrades across the BitChat network.

### Where is the packet type 0x21 actually used in the BitChat codebase?

The constant `authenticatedPeerState = 0x21` is defined in [`bitchat/Protocols/BitchatProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Protocols/BitchatProtocol.swift) and referenced in [`bitchat/Services/BLE/BLENoisePayloadFactory.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLENoisePayloadFactory.swift) to tag the payload type when wrapping the encoded `AuthenticatedPeerStatePacket` for BLE transmission, allowing the receiving end to route the data to the appropriate decoder.