I'm working with a linux computer that will have no way to interface with it when it is deployed.
The only (end user friendly) access point it will have to start off is a bluetooth access point.
Bluetooth communication has been succesfully established, and a phone can connect with the controller without having to do any input on the controller.
The problem is that it isn't safe in this situation, anything can now freely pair and connect.
I would like the controller to request a controller unique pin from the connecting device so only people with access to that pin can connect with the controller.
I'm using python to manage the agent that handles the pairing process, and I'm currently using this code:
import dbus.service
import dbus.mainloop.glib
from gi.repository import GLib
BUS_NAME = 'org.bluez'
ADAPTER_IFACE = 'org.bluez.Adapter1'
ADAPTER_ROOT = '/org/bluez/hci'
AGENT_IFACE = 'org.bluez.Agent1'
AGNT_MNGR_IFACE = 'org.bluez.AgentManager1'
AGENT_PATH = '/my/app/agent'
AGNT_MNGR_PATH = '/org/bluez'
CAPABILITY = 'KeyboardDisplay'
DEVICE_IFACE = 'org.bluez.Device1'
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
def set_trusted(path):
props = dbus.Interface(bus.get_object(BUS_NAME, path), dbus.PROPERTIES_IFACE)
props.Set(DEVICE_IFACE, "Trusted", True)
class Agent(dbus.service.Object):
@dbus.service.method(AGENT_IFACE,
in_signature="", out_signature="")
def Release(self):
print("Release")
@dbus.service.method(AGENT_IFACE,
in_signature='o', out_signature='s')
def RequestPinCode(self, device):
print(f'RequestPinCode {device}')
return '0000'
@dbus.service.method(AGENT_IFACE,
in_signature="ou", out_signature="")
def RequestConfirmation(self, device, passkey):
print("RequestConfirmation (%s, %06d)" % (device, passkey))
set_trusted(device)
return
@dbus.service.method(AGENT_IFACE,
in_signature="o", out_signature="")
def RequestAuthorization(self, device):
print("RequestAuthorization (%s)" % (device))
auth = input("Authorize? (yes/no): ")
if (auth == "yes"):
return
raise Rejected("Pairing rejected")
@dbus.service.method(AGENT_IFACE,
in_signature="o", out_signature="u")
def RequestPasskey(self, device):
print("RequestPasskey (%s)" % (device))
set_trusted(device)
passkey = input("Enter passkey: ")
return dbus.UInt32(passkey)
@dbus.service.method(AGENT_IFACE,
in_signature="ouq", out_signature="")
def DisplayPasskey(self, device, passkey, entered):
print("DisplayPasskey (%s, %06u entered %u)" %
(device, passkey, entered))
@dbus.service.method(AGENT_IFACE,
in_signature="os", out_signature="")
def DisplayPinCode(self, device, pincode):
print("DisplayPinCode (%s, %s)" % (device, pincode))
class Adapter:
def __init__(self, idx=0):
bus = dbus.SystemBus()
self.path = f'{ADAPTER_ROOT}{idx}'
self.adapter_object = bus.get_object(BUS_NAME, self.path)
self.adapter_props = dbus.Interface(self.adapter_object,
dbus.PROPERTIES_IFACE)
self.adapter_props.Set(ADAPTER_IFACE,
'DiscoverableTimeout', dbus.UInt32(0))
self.adapter_props.Set(ADAPTER_IFACE,
'Discoverable', True)
self.adapter_props.Set(ADAPTER_IFACE,
'PairableTimeout', dbus.UInt32(0))
self.adapter_props.Set(ADAPTER_IFACE,
'Pairable', True)
if __name__ == '__main__':
agent = Agent(bus, AGENT_PATH)
agnt_mngr = dbus.Interface(bus.get_object(BUS_NAME, AGNT_MNGR_PATH),
AGNT_MNGR_IFACE)
agnt_mngr.RegisterAgent(AGENT_PATH, CAPABILITY)
agnt_mngr.RequestDefaultAgent(AGENT_PATH)
adapter = Adapter()
mainloop = GLib.MainLoop()
try:
mainloop.run()
except KeyboardInterrupt:
agnt_mngr.UnregisterAgent(AGENT_PATH)
mainloop.quit()
I got this code from here, but to be honest, I don't understand it.
It defines a bunch of dbus.service.method's and I think it somehow chooses one or something.
As someone who is used to less abstract languages I just dont know where its putting all this stuff or where its pulling.
I believe it just uses the RequestConfirmation() method in this case. so all the others are just junk.
But I would like to know if/how I can modify this to make it ask for a pin or passkey on my phone when I try to pair with the controller.
To try to explain what the code is doing...
There is a Bluetooth daemon (bluetoothd
) running on a Linux system. bluetoothd
handles the Bluetooth functionality on the system. Below is a sequence diagram of the interactions with bluetoothd
for pairing.
RegisterAgent()
is used to tell bluetoothd
where the D-Bus org.bluez.Agent1
interface has been created and which capability to use this registered agent for.
When a pairing request comes in to bluetoothd
it calls RequestConfirmation()
at the registered location. If the returned value back is empty then the confirmation is confirmed. If there is either org.bluez.Error.Rejected
or org.bluez.Error.Canceled
thrown then the pairing is rejected.
Which of the org.bluez.Agent1
methods gets called by bluetoothd
will depend on exactly what type of pairing request has been received.
The capability setting is probably best summarized by this table:
If it is the RequestConfirmation()
method that is getting called, then the device D-Bus path and a uint32 passkey are the parameters passed in. It is up to the method in the agent to decide, on that information, if it should accept the request or not.
The org.bluez.Agent1
interface is documented at:
https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/agent-api.txt
The official BlueZ example for a simple agent is at:
https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/test/simple-agent