Skip to content

fix: handle BLE LongTermKey in device info parsing - #34

Open
Hans-Kerman wants to merge 1 commit into
x2es:masterfrom
Hans-Kerman:fix/linkkey-section-error
Open

fix: handle BLE LongTermKey in device info parsing#34
Hans-Kerman wants to merge 1 commit into
x2es:masterfrom
Hans-Kerman:fix/linkkey-section-error

Conversation

@Hans-Kerman

Copy link
Copy Markdown

Problem Statement

When using bt-dualboot on a Surface Pro 7 dual-booting Arch Linux and Windows 10,
bluetooth devices (particularly Microsoft Modern Mouse) failed to sync between OSes.
The tool threw configparser.NoSectionError: No section: 'LinkKey' because BLE devices
use LongTermKey instead of traditional LinkKey for pairing credentials.

Solution

The key modification implements fallback mechanism for different pairing key types:

  1. Updated extract_info() in bt_linux/bluetooth_device_factory.py through AI-assisted implementation
  2. Added section validation checking both [LinkKey] and [LongTermKey] sequentially
  3. Introduced error handling for missing key sections

Implementation Notes

  • Core logic was developed with guidance from AI tools
  • Code has been manually validated but requires expert review
  • Limited maintenance capability from submitter (Linux novice) - future maintenance welcome

Validation

After only initial verification, it can now recognize my mouse.

Notes

As a first-time contributor to open-source projects, I appreciate any feedback on
implementation details or testing requirements. Please let me know if additional
changes are needed.
In fact, this PR is also mostly generated by AI

@jimnoneill

Copy link
Copy Markdown

Cross-linking a related writeup from the linux-surface project: linux-surface/linux-surface#2060 — it adds a standalone Python helper (sync-ble-pairing-from-windows.py) that parses a reged -x export from Windows and writes a BlueZ info file for a single chosen LE device, plus a full README with byte-order conventions empirically verified via btmon HCI traces. Some of the findings there might be useful for anyone who wants to pick this PR back up and extend it to full LE support in bt-dualboot proper.

The main things I found while doing this manually that aren't in the current diff here:

  1. Windows side needs subkey traversal too. bt_dualboot/bt_windows/devices.py:get_devices() only reads adapter-level "MAC"=hex:… values (Classic Bluetooth link keys). LE pairings live in subkeys under the adapter key — e.g. ...\BTHPORT\Parameters\Keys\<adapter-hex>\<device-hex>\{LTK, IRK, ERand, EDIV, Address, AddressType, AuthReq}. To sync an LE device end-to-end, bt-dualboot would need to enter those subkeys too, not just the BR/EDR values. The current PR fixes the Linux-side reader (bt_linux/bluetooth_device_factory.py), which is necessary but not sufficient — the Windows side still needs work for the windows -> linux direction.

  2. BluetoothDevice data model needs more than pairing_key. An LE pairing needs at minimum LTK, EDIV, Rand, and Authenticated; most also need IRK for RPA-rotating peripherals; some Legacy pairings additionally need a [SlaveLongTermKey] block. A single pairing_key string can't round-trip any of this. The model probably needs a discriminated-union LinkKey | LongTermKey { ltk, ediv, rand, irk?, authenticated, slave_ltk? } or similar.

  3. Byte-order conventions differ between IRK and LTK, counterintuitively. IRK is byte-reversed between the Windows registry and BlueZ's hex string; LTK is not. Reversing LTK (by analogy with IRK, which is the obvious thing to try) produces a characteristic failure signature: HCI LE Start Encryption succeeds at the command level, then Encryption Change Status: Connection Timeout (0x08) comes back, the peripheral issues an SMP: Security Request asking for a fresh pairing, and the kernel disconnects with Reason: Authentication Failure (0x05). I initially assumed symmetric reversal and got exactly that loop; keeping Windows LTK bytes in their original order fixed it. The diagnostic trace via sudo btmon -w trace.snoop while reproducing the failure is reliable if you're trying to verify on another device.

  4. Authenticated can't be inferred from the Windows-stored AuthReq byte. The AuthReq dword in the Windows registry is the central's capability request (e.g. 0x2d = Bonded, MITM, SC-supported, CT2), not the negotiated pairing mode. A peripheral that only supports Legacy will respond with its own AuthReq (SC bit clear) and the session ends up Legacy even though Windows's stored value says SC was requested. For a sync tool, the safest default is Authenticated=1 (Legacy) with an override flag for LESC devices, and the user can flip if encryption fails — the failure mode is always the timeout-disconnect loop above, never data corruption.

  5. [SlaveLongTermKey] is a Legacy-only thing. Modern LE Legacy peripherals mostly use a single symmetric LTK, but a few older ones split the direction. Windows only stores one LTK per device in the registry, so sync'ing to a device that needs a slave LTK isn't straightforwardly possible without additional per-device workarounds.

  6. Don't use chntpw -e with a heredoc to read the Windows hive. chntpw's interactive REPL doesn't exit on EOF — it loops and re-prints its prompt forever. If the parent shell's stdout is redirected to a file (e.g. a subprocess that captures output), the file grows at tens of MB/s until the disk fills. Worse, killing the parent bash with SIGKILL can leave chntpw as an orphan holding the fd to a now-deleted file, so df and du disagree until you find and kill the orphan by PID. reged -x (non-interactive, reads/writes cleanly) is the right tool for the job; for writes, reged -I -C. The linked PR's README documents this.

None of this is criticism of the existing diff — it's a solid fix for the immediate configparser.NoSectionError that blocks LE devices entirely, and landing it would be strictly better than the current state. I'm posting this mainly so whoever picks up the next iteration (or anyone else hitting LE sync issues and searching the repo) has the information in one place.

Happy to take ownership of a more complete follow-up PR if the maintainers are open to it, but the upstream activity here suggests it might end up as a fork — so the immediate path for affected users is the linked linux-surface helper and writeup.

@armujahid armujahid mentioned this pull request May 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants