Debugging Bluetooth LE is tricky, and on mobile devices, it can be even more so!
In this guide, I lay out the different steps in detail on how you can debug and sniff Bluetooth LE communication on iOS devices. Specifically, we’ll be capturing the HCI commands and events between the Host and Controller on the iOS device.
Host Controller Interface (HCI)
Before we dive into the details of how to achieve this, let’s go over a few essential concepts.
The Host Controller Interface (HCI) layer is a communication interface defined in the Bluetooth Core Specification document. It standardizes the way that a Bluetooth Host communicates with a Bluetooth Controller.
It is an optional interface that is needed in cases where the controller and host are present on separate hardware components. If it is omitted, it means in such a device, the same processor will implement the functionality of a host and a controller. Here’s an example:
If it is included, then it allows a device to implement the Bluetooth LE functionality using a host from one vendor and a controller from another vendor (on different chipsets). Physically, the HCI may run on top of an interface like UART, RS-232, USB, or SD. The set of packets that can be exchanged on this interface is defined by the Bluetooth specification.
Examples of where the Host and Controller are separated across different microcontrollers/processors, and HCI is implemented include:
A. Bluetooth USB dongle connected to a PC:
B. Smartwatch
The different aspects of HCI include:
- HCI Commands: these are sent from the Host to the Controller to request an operation. Examples include creating a connection, starting advertising, stopping advertising, starting scanning, stopping scanning, creating CIS, creating BIS, etc.
- HCI Events: these are sent from the Controller to the Host, reporting back with important information such as the status of a command. Examples include connection complete, advertising report, scan request received, etc.
Different operations in Bluetooth LE are split across the Host and Controller. Some are the Host’s responsibility, while others are the Controller’s. If we can get a peek at the commands and events that are being passed between the two subsystems, this gives us developers a lot more insight into what’s happening in a Bluetooth LE device (or app).
Many might not know that both Android and iOS actually expose the HCI layer to us (I certainly didn’t realize this until later in my Bluetooth LE development journey!). This is a powerful feature that can be very useful in many debugging scenarios.
For example, did you know that:
- You can capture the GATT operations being triggered by any mobile app?
- Or, that you can capture the advertising reports from the smartphone’s Controller when scanning for Bluetooth LE devices?
- Or, that you can capture the security keys used to secure communication between the smartphone and another Bluetooth LE device? and then use that to decrypt the encrypted data exchanged between the two devices?!
All of these are possible on both iOS and Android. In this post, we’ll cover how to do this on iOS. In a follow-up article, we’ll cover the Android case.
Requirements
To be able to do this on iOS, we need the following:
- A macOS laptop/desktop
- An Apple Developer Account
- An iOS device running iOS 13 or later
- A cable for connecting the iOS device to the Mac
HCI Capture Steps on iOS
Here are the steps to take to capture HCI communication on iOS:
Step 1: Download and install Apple’s Bluetooth Profile on an iOS device. You can do this in two ways:
- Download the Bluetooth for iOS Profile directly on your iOS device
- Or you can download it on your Mac, AirDrop it to your iOS device, and then open it there.
Here’s a short screen recording detailing this step:
Once installed, you’ll be able to find it under Settings → General → VPN & Device Management:
Step #2: Now that we have the Profile installed on our iOS device, we can move on to the next step, which is to download and run a tool from Apple called the Packet Logger. You can download the Packet Logger tool from the Additional Tools for Xcode package here [simply download the latest version].
Step #3: Now, let’s run the Packet Logger tool (you can also drag and drop it to your local Applications folder so you can quickly run it next time).
Step #4: Next, connect your iOS device to your Mac (and accept any dialogs asking to allow access to your device).
Step #5: Click on the File → New iOS Trace menu item:
Step #6: After starting the iOS Trace, you should start seeing some Bluetooth traffic, and the iOS device should show a “signal” status at the top of the screen:
Step #7: As an advanced tip, you can actually export the log file to a BTSnoop format, which can be opened in Wireshark for additional analysis.
💡 BTSnoop is a file format defined for storing a log of Bluetooth HCI traffic (commands
and events). Wireshark and many different sniffer software packages support this file format for analysis.
Step #8: So, what do you do from here now that you have access to the HCI logs?
At this point, you can view all the HCI traffic between the Bluetooth Host and Controller on your iOS device.
In order to understand the different HCI commands and events, I recommend referring to the Bluetooth Core Specification document Vol 4, Part E, Section 7. Here are examples of some common Bluetooth LE HCI Commands on the Central side (the most common role for a smartphone):
- LE Set Scan Parameters command
- LE Set Scan Enable command
- LE Create Connection command
- LE Enable Encryption command
Capturing BLE Security Keys (LTK)
One handy use case for the Packet Logger tool is to extract the LTK for a secure connection between two BLE devices (utilizing the LE Secure Connections pairing method).
To demonstrate this, I am going to use the following:
- A Nordic nRF52840 board running the nRF Connect SDK (Zephyr-based) peripheral_sc_only example (this one runs in LE Secure Connections only mode)
- nRF Connect mobile app on an iPhone 14 Pro Max (running iOS 17)
- Ellisys Bluetooth Tracker (Bluetooth LE Sniffer tool)
[Used for sniffing over the air traffic and inputting the LTK for traffic decryption. Alternatively, you can use Wireshark in combination with nRF Sniffer on one of the Nordic dev boards]
Exercise Steps
Make sure the nRF DK is not bonded with the iOS device. If it already is, then remove it from the Bluetooth Settings menu by clicking on the (i) icon and then clicking “Forget This Device”:
Now navigate to the nRF Connect mobile app and discover the device:
Note: Make sure the Packet Logger Tool is running an iOS Trace before continuing with the next step.
Click “Connect.” then, you’ll be presented with a Passkey entry form:
You’ll find the passkey displayed in the serial terminal from the nRF DK:
Make sure you enter the passkey reasonably quickly. Otherwise, the pairing process will time out. Once paired, the device serial terminal output will indicate a successful pairing:
Once this is completed, you can now stop the Packet Logger trace and filter for HCI Commands:
Now, look for the HCI Command “LE Start Encryption” and expand the command for more information. In there, you will find the LTK for the bond. Voila!
Even if you miss capturing the pairing process using the Packet Logger, you can disconnect the nRF DK (or any other device you’re looking to capture) and capture the reconnection operation to the DK.
From there, you can again look for the same HCI command: “LE Start Encryption” (you can also verify this is the same LTK used when first establishing the pairing between the two devices):
After extracting the LTK from the Packet Logger tool, you can now fully decrypt the traffic between the two BLE devices (the nRF DK and the iOS device)!
Summary
In this post, we covered a very practical technique that we can use when debugging Bluetooth LE communication with a smartphone, whether that’s from an app that you’ve developed or even any other app on your phone!