Search code examples
bluetoothandroid-bluetoothbluez

Android pairing to custom USB bluetooth adapter


Is it possible to force a legacy fixed pin entry when pairing an android to a custom USB adapter, such as the DKBT111. Looking at SSP and the "Just works" pairing, this doesn't seem to satisfy the security requirement I am working with.

I would like to guarantee no one can attempt to pair with the USB device while it is discoverable without a fixed, preset PIN. I am having trouble configuring the controller using bluetoothctl and Bluez. The best I can get is the 6 digit passkey comparison, but I won't have a display for the code on the box the usb will be connected to.

What settings do I have to change to be able to set a PIN for the server, and any phone needs to type it in to pair?

I am using Bluez on tinycore.


Solution

  • To achieve such scenario you may need to implement custom agent. As you don't have Display, you have to choose one of the capability of your device from the options.

    Bluez always expects minimum 6 digit PIN key, if you want to restrict yourself to 4 digit PIN, then you should pad using "0"'s as specified here.

    For your custom agent, you need to implement either "DisplayPinCode" or "DisplayPasskey" method returning fixed PIN and registering it using "RegisterAgent".

    For restricting only your Android device, you can compare your device MAC address in DisplayPinCode/DisplayPasskey, which gets the MAC address of the device which is requesting pairing as "First Argument object device".

    Note that the "object device" is MAC address as object path i.e /org/bluez/hciX/dev_AA_BB_CC_XX_YY_ZZ format.

    #include <glib.h>
    #include <gio/gio.h>
    #include "agent.h"
    
    #define AGENT_PATH  "/org/bluez/AutoPinAgent"
    
    static void bluez_agent_method_call(GDBusConnection *conn,
                        const gchar *sender,
                        const gchar *path,
                        const gchar *interface,
                        const gchar *method,
                        GVariant *params,
                        GDBusMethodInvocation *invocation,
                        void *userdata)
    {
        g_print("Agent method call: %s.%s()", interface, method);
    }
    
    static const GDBusInterfaceVTable agent_method_table = {
        .method_call = bluez_agent_method_call,
    };
    
    int bluez_register_agent(GDBusConnection *con)
    {
        GError *error = NULL;
        guint id = 0;
        GDBusNodeInfo *info = NULL;
    
        static const gchar bluez_agent_introspection_xml[] =
            "<node name='/org/bluez/SampleAgent'>"
            "   <interface name='org.bluez.Agent1'>"
            "       <method name='Release'>"
            "       </method>"
            "       <method name='RequestPinCode'>"
            "           <arg type='o' name='device' direction='in' />"
            "           <arg type='s' name='pincode' direction='out' />"
            "       </method>"
            "       <method name='DisplayPinCode'>"
            "           <arg type='o' name='device' direction='in' />"
            "           <arg type='s' name='pincode' direction='in' />"
            "       </method>"
            "       <method name='RequestPasskey'>"
            "           <arg type='o' name='device' direction='in' />"
            "           <arg type='u' name='passkey' direction='out' />"
            "       </method>"
            "       <method name='DisplayPasskey'>"
            "           <arg type='o' name='device' direction='in' />"
            "           <arg type='u' name='passkey' direction='in' />"
            "           <arg type='q' name='entered' direction='in' />"
            "       </method>"
            "       <method name='RequestConfirmation'>"
            "           <arg type='o' name='device' direction='in' />"
            "           <arg type='u' name='passkey' direction='in' />"
            "       </method>"
            "       <method name='RequestAuthorization'>"
            "           <arg type='o' name='device' direction='in' />"
            "       </method>"
            "       <method name='AuthorizeService'>"
            "           <arg type='o' name='device' direction='in' />"
            "           <arg type='s' name='uuid' direction='in' />"
            "       </method>"
            "       <method name='Cancel'>"
            "       </method>"
            "   </interface>"
            "</node>";
    
        info = g_dbus_node_info_new_for_xml(bluez_agent_introspection_xml, &error);
        if(error) {
            g_printerr("Unable to create node: %s\n", error->message);
            g_clear_error(&error);
            return 0;
        }
    
        id = g_dbus_connection_register_object(con, 
                AGENT_PATH,
                info->interfaces[0],
                &agent_method_table,
                NULL, NULL, &error);
        g_dbus_node_info_unref(info);
        //g_dbus_connection_unregister_object(con, id);
        return id;
    }
    

    The above example is incomplete template without any specific methods. You need to implement "DisplayPinCode/DisplayPasskey" inside "bluez_agent_method_call" based on "method" name, which comes as argument.

    EDIT: Same example for Fixed PIN with more details answered in this question. Adding it for future reference and completeness.