Pairing¶
Pairing requires that the host and client exchange encryption keys in order to communicate securely.
Agents¶
Hint
Some devices use “out of band” data (for example communicated using NFC) to verify pairing. This approach is currently unsupported by bluez_peripheral agents.
Warning
By default bluez_peripheral agents are registered as default (see the register() function default argument). This generally requires superuser permission. If an agent is not registered as default it will not be called in response to inbound pairing requests (only those outbound from the source program).
An agent is a program used to authorize and secure a pairing. Each agent has associated Input/ Output capabilities (see AgentCapability) which are exchanged at the start of the pairing process. Devices with limited IO capabilities cannot support authentication which prevents access to attributes with certain flags (see Pairing Security).
Using an Agent¶
Hint
The “message bus” referred to here is a dbus_fast.aio.MessageBus.
There are three potential sources of agents:
Bluez supports a number of built in agents. You can select an agent with given capability by using the following command in your terminal:
bluetoothctl agent <capability>
These agents are unreliable but the simplest to set up.
bluez_peripheral includes built in NoIoAgent and YesNoAgent agents which can be used as below:
from bluez_peripheral import get_message_bus
from bluez_peripheral.agent import NoIoAgent
async def agent_builtin():
bus = await get_message_bus()
agent = NoIoAgent()
# By default agents are registered as default.
await agent.register(bus, default=True)
# OR
def accept_pairing(code: int) -> bool:
# TODO: Show the user the code and ask if it's correct.
# if (correct):
# return True
# else:
# return False
return True
def cancel_pairing():
# TODO: Notify the user that pairing was cancelled by the other device.
pass
async def agent_custom():
agent = YesNoAgent(accept_pairing, cancel_pairing)
await agent.register(bus)
if __name__ == "__main__":
asyncio.run(agent_builtin())
asyncio.run(agent_custom())
Support for custom agents in bluez_peripheral is limited. The recommended approach is to inherit the bluez_peripheral.agent.BaseAgent in the same way as the built in agents. The bluez_peripheral.agent.TestAgent can be instanced as shown for testing:
from bluez_peripheral import get_message_bus
from bluez_peripheral.agent import TestAgent
async def main():
bus = await get_message_bus()
agent = TestAgent()
await agent.register(bus)
if __name__ == "__main__":
asyncio.run(main())
The test agent will then fire breakpoints when each of the interfaces functions is called during the pairing process. Note that when extending this class the type hints as used are important (see dbus_fast services).
Debugging¶
Pairing can be quite difficult to debug. In between testing attempts ensure that the peripheral has been unpaired from the host and vice versa. Using linux you can list paired devices using bluetoothctl list then remove any unwanted devices using bluetoothctl remove <device id>. Additionally the linux bluetooth daemon stores persistent adapter metadata in the /var/lib/bluetooth/ (see the bluetoothd manpages).
Pairing Security¶
Responder |
Initiator |
||||
|---|---|---|---|---|---|
Display Only |
Display YesNo |
Keyboard Only |
NoInput NoOutput |
Keyboard Display |
|
Display Only |
Just Works |
Just Works |
Passkey Entry |
Just Works |
Passkey Entry |
Display YesNo |
Just Works |
Numeric Comparison (Just Works*) |
Passkey Entry |
Just Works |
Numeric Comparison (Passkey Entry*) |
Keyboard Only |
Passkey Entry |
Passkey Entry |
Passkey Entry |
Just Works |
Passkey Entry |
NoInput NoOutput |
Just Works |
Just Works |
Just Works |
Just Works |
Just Works |
Keyboard Display |
Passkey Entry |
Numeric Comparison (Passkey Entry*) |
Passkey Entry |
Just Works |
Numeric Comparison (Passkey Entry*) |
For completeness these pairing models are described below:
Just Works - Devices may pair with no user interaction (eg a phone connecting to a headset without a display). Since this has no MITM protection, connections established using this model may not perform authentication (ie. access authenticated attributes).
Numeric Comparison - The user verifies 6 digit codes displayed by each device match each other.
Passkey Entry - The user is shown a 6 digit code on one device and inputs that code on the other.
Out of Band - A MITM resistant channel is established between the two devices using a different protocol (eg NFC).
Note that IO Capability is not the only factor in selecting a pairing algorithm. Specifically:
Where neither device requests Man-In-The-Middle (MITM) protection, Just Works pairing will be used.
Where both devices request it, OOB pairing will be used.
See also
- Bluetooth SIG Pairing Overview
- Bluetooth Core Spec v5.2
Vol 3, Part H, Table 2.8 (source of Pairing Security)
- Bluez Documentation