HID report descriptor (with iPhone)

5.4k views Asked by At

I am trying to send keystrokes via bluetooth to my iPhone4 from ubuntu. I.e., developing a very primitive virtual keyboard.

Problem Have my application talk to iPhone (i.e. using report protocol, that's all iPhone supports). As it is, my write(interruptChannelFD) calls return no errors, but no text appears on iPhone side. The l2cap channels have been opened using blueZ library.

Question 1: Given that no virtual keyboard exists that does that, just how hard is it?

I am at the stage where iPhone and my linux box connect and stay connected, no problem. Also, all the perror() calls are telling me that control and interrupt channel sockets connect just fine. (What I do is hciconfig my dongle to Keyboard device class and use the well-known code by Collin Mulliner with slight modification -- I had to enter a passcode once, as all smartphones require).

Question 2: I should be able to just write() into interrupt socket without worrying about encryption, right? I've entered the passcode and the phone trusts the keyboard. (Collin was pondering a possible hid attack, but I connect honestly)

Also, it is my understanding that in boot protocol of a HID device, the exact report descriptor specfied in SPD is hardly relevant -- the report format is fixed anyway. So...

Question 3: Do I miss something major about the report protocol. What I do is modify the Apple keyboard report descriptor and write into the socket (see below).

const uint8_t hid_spec[] = { 
    0x05, 0x01, // usage page
    0x09, 0x06, // keyboard
    0xa1, 0x01, // collection (Application)
    0x85, 0x01, // report id (0x01)
    0x05, 0x07, // usage page(keyboard)
    0x19, 0xe0, // usage min
    0x29, 0xe7, // usage max
    0x15, 0x00, // logical min
    0x25, 0x01, // logical max
    0x75, 0x01, // report size
    0x95, 0x08, // report count
    0x81, 0x02, // input (dat var abs)

    0x75, 0x08, // report size 
    0x95, 0x01, // report count 
    0x81, 0x01, // input (const)

            // The following two outputs I don't seem to receive
    0x75, 0x01, // report size
    0x95, 0x05, // report count
    0x05, 0x08, // usage page (LEDs)
    0x19, 0x01, // usage min
    0x29, 0x05, // usage max 
    0x91, 0x02, // OUTPUT1 (dat var abs) 

    0x75, 0x03,
    0x95, 0x01,
    0x91, 0x01, // OUTPUT2 (arr,const) 

    0x75, 0x08, // report size
    0x95, 0x06, // report count
    0x15, 0x00, // logical min
    0x26, 0xff, 0x00 // logical max

    0x05, 0x07
    0x19, 0x00
    0x2a, 0xff, 0x00,
    0x81, 0x00,

            // A total of 9 bits sent by now
            // I tried remove the following fields
            /********** BEGIN SNIP
    0x75, 0x01,
    0x95, 0x01,
    0x15, 0x00,
    0x25, 0x01,

    0x05, 0x0c,
    0x09, 0xb8,
    0x81, 0x06,


    0x09, 0xe2,
    0x81, 0x06,


    0x09, 0xe9,
    0x81, 0x02,
    0x09, 0xea,
    0x81, 0x02,
    0x75, 0x01,
    0x95, 0x04,
    0x81, 0x01,
            **** END SNIP/

    0xc0         // end coll

};

After this, I write the following 10 bytes into the interrupt channel:

                    pkg[0] = 0xa1;   // BT HDR (DATA)
                    pkg[1] = 0x01;   // REPORT ID 0x1 == kbd
                    pkg[2] = modifiers; // Ctrl, Shift, etc
                    pkg[3] = 0x00;    // constant 0 (see descr)
                    // pkg[4] = 0x00; // the key code - entered before this point, according to HID usage tables.
                    pkg[5] = 0x00;
                    pkg[6] = 0x00;
                    pkg[7] = 0x00;
                    pkg[8] = 0x00;
                    pkg[9] = 0x00;

                    if (write(is, pkg, 10) <= 0) {
                        perror("write");
                        exit(-1);
                    }
1

There are 1 answers

1
Lubomir Rintel On

Good day, sir.

Let me kindly point to you to a humble holiday timewaster of mine that actually is able to be used with a piece of junk called iPad, whose software stack should be reasonable close to iPhone of yours: https://github.com/lkundrak/virtkbd

Aside from the actual implementation, I'll try to answer your questions.

Question 1:

Given the quality and length of Bluetooth HID profile specification, USB HID one and tooling available and actual device specifics, I guess trial and error will take you further. Have an actual Bluetooth keyboard, and write a simple protocol repeater and dumper to let you observe what do they do -- refer to documentation to decrypt what's going on.

Question 2:

Right. For my iPad, I first need to pair the devices with my computer not being of a Keyboard class (my guess is that iPad otherwise attempts to make me enter a PIN, which can not be done with Bluez). Then I need to change the class to Keyboard (by running my bithdd program) and force iPad to reconnect, so that it grabs the protocol descriptor from SDP and attempts to connect to L2CAP ports 17 and 19.

Question 3:

Yes, it's pretty much like that -- I don't think you miss anything important.

Have a nice day!