Linux/Windowsのデュアルブート環境でBluetoothやBLEのデバイスを共有する方法

私のPCは普段使いのGentoo LinuxとゲームとかCADとか用のWindowsの両方をインストールしてあります。 そんなデュアルブート環境だと、OSを変えるたびにBluetoothデバイスを再ペアリングする必要があってちょっと面倒臭いことになります。

この原因は、ペアリングのときに生成されるキーをOS間で共有出来ないことにあるようです。 デバイスから見ると、「同じMACアドレスを名乗っているのに約束していたキーと違う。偽物だ!」みたいな感じらしいです。

調べてみたら結構簡単に合せられるっぽいので、設定してみました。 Bluetoothの場合とBluetooth LE (以下BLE)の場合で一部手順が違うので、お使いのデバイスに合わせて対応してください。

BluetoothとBLEのどちらも、全体としては以下のような流れで作業します。

  1. Linux側でペアリングする: 接続情報のファイルを生成するために、まずはLinux側で接続します。
  2. Windows側でペアリングする: Windows側でも接続して、最終的に使うキーを生成します。
  3. Windowsのレジストリから情報を取り出す: Windowsが作った接続に必要な情報を取り出します。
  4. Linuxに戻って接続情報を編集する: 3で取り出した情報を加工して、1で作ったファイルに書き込みます。

1. Linux側でペアリングする

まずはLinuxを起動して、対象のデバイスと普通にペアリングをします。 bluetoothctlやbluemanなどを使っていつも通り接続してください。

接続すると、/var/lib/bluetooth/{レシーバのmacアドレス}/{デバイスのmacアドレス}の中に接続情報が書かれたファイルが生成されるはずです。

2. Windows側でペアリングする

今度はWindowsを起動して、もう一度ペアリングします。

ここでWindowsとデバイス間で作られたキーの情報を使って接続するようにします。 別にLinux側で作ったやつをWindows側に持っていっても良いはずなのですが、Linux側の方がいじりやすいのでこの手順にしています。

3. Windowsのレジストリから情報を取り出す

2021-07-09 追記

新しいデバイスを買ったので同じことをやろうとしたら、必要な情報を見つけられませんでした。 どうやらWindowsのアップデートか何かで仕様が変わったようです。

下記の方法で見つけられない場合、この記事の末尾にある別の方法が使えるかもしれません。

Windowsが作ったキーが欲しいので、regedit.exeを使ってレジストリの以下の場所にある情報をエクスポートします。

HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\BTHPORT\Parameters\Keys

これを適当なエディタで開いて確認してみると、以下のような内容になっているはずです。 文字コードがUTF-16LEなので注意。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\BTHPORT\Parameters\Keys]

[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\BTHPORT\Parameters\Keys\001bdcxxxxxx]
"MasterIRK"=hex:08,ce,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx
"cc988bxxxxxx"=hex:84,ed,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx

[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\BTHPORT\Parameters\Keys\001bdcxxxxxx\d3bc56xxxxxx]
"LTK"=hex:a8,76,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,f8
"KeyLength"=dword:00000010
"ERand"=hex(b):eb,00,00,00,00,00,00,89
"EDIV"=dword:00ab0123
"MasterIRKStatus"=dword:00000001
"AuthReq"=dword:0000002d

4. Linuxに戻って接続情報を編集する

Linuxに戻って、/var/lib/bluetooth/{レシーバのmacアドレス}/{デバイスのmacアドレス}/infoを開きます。 レシーバとデバイスのmacアドレスは以下のコマンドで見付けられると思います。

# レシーバの一覧
$ bluetoothctl list
Controller 00:1B:DC:XX:XX:XX hostname [default]

# ペアリングしているデバイスの一覧
$ bluetoothctl devices
Device D3:BC:56:XX:XX:XX Microsoft Bluetooth Mouse
Device CC:98:8B:XX:XX:XX WH-1000XM3

ここからは、Bluetoothの場合とBluetooth LEの場合で分岐します。 開いたinfoファイルの中に[LinkKey]という記述がある場合はBluetoothの場合に、[LongTermKey]という記述がある場合はBluetooth LEの場合に進んでください。

Bluetoothの場合

必要な情報を探す

Windows側でエクスポートした.regファイルから、以下のようなセクションを見つけます。

[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\BTHPORT\Parameters\Keys\001bdcxxxxxx]
"MasterIRK"=hex:08,ce,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx
"cc988bxxxxxx"=hex:84,ed,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx

Keys\001bdcxxxxxxの部分がレシーバのmacアドレスになっているはずです。 この中からデバイスのmacアドレスと対応する値を見つけ出してコピーします。

今回は"cc988bxxxxxx"とペアリングしたいので、84,ed,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xxの部分をコピーします。 カンマは不要なので削除して、ついでにLinux側に合わせて大文字にしておきます。(小文字でも別に良いっぽいけれど)

ファイルに反映する

コピーしてカンマを消した値を使って、Linux側のinfoファイルを以下のような感じで編集します。

[LinkKey]
Key=84EDXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Type=4
PINLength=0

これでキーの移行は完了です。

Bluetooth LEの場合

必要な情報を探す

Windows側でエクスポートした.regファイルから、以下のようなセクションを見つけます。 Bluetoothの場合と違って結構長いです。

[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\BTHPORT\Parameters\Keys\001bdcxxxxxx\d3bc56xxxxxx]
"LTK"=hex:a8,76,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,f8
"KeyLength"=dword:00000010
"ERand"=hex(b):eb,00,00,00,00,00,00,89
"EDIV"=dword:00ab0123
"MasterIRKStatus"=dword:00000001
"AuthReq"=dword:0000002d

Keys\001bd0cxxxxxxの部分がレシーバのmacアドレスで、その後に続く\d3bc56xxxxxxの部分がデバイスのmacアドレスになっています。 複数デバイスある場合はこのセクションが複数あるはずなので、必要なものを見つけてください。

加工する

ここで必要なのは以下の3つの情報です。 ついでに色々加工をします。

  • LTK: hex:の後ろのa8,76,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,f8からカンマを取って、A876XXXXXXXXXXXXXXXXXXXXXXXXXXF8のようにして使います。
  • ERand: hex(b):の後ろのeb,00,00,00,00,00,00,89を左右反転(89,00,00,00,00,00,00,eb)してカンマを取って、10進数(9871890383196127467)にして使います。
  • EDIV: dword:の後ろの00ab0123を10進数にして、11206947のようにして使います。

諸々の変換はPythonとか使うと楽かもしれません。

>>> # LTKの加工
>>> 'a8,76,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,f8'.replace(',', '').upper()
A876XXXXXXXXXXXXXXXXXXXXXXXXXXF8

>>> # ERandの加工
>>> int(''.join(reversed('eb,00,00,00,00,00,00,89'.split(','))), 16)
9871890383196127467

>>> # EDIVの加工
>>> int('00ab0123', 16)
11206947

ファイルに反映する

加工した情報を使って、Linux側のinfoファイルのLongTermKeyセクションを以下のように書き換えます。

[LongTermKey]
Key=A876XXXXXXXXXXXXXXXXXXXXXXXXXXF8
Authenticated=0
EncSize=16
EDiv=11206947
Rand=9871890383196127467

値はそれぞれ、LTKKeyERandRandEDIVEDivになります。

5. Bluetoothデーモンを再起動して確認する

これで、Windows側で生成したキーをLinux側でも使えるようになっているはずです。 変更を反映させるために、デーモンを再起動させましょう。

$ sudo systemctl restart bluetooth

問題無く設定出来ていれば、再ペアリングすることなく両方のOSから接続出来るようになっているはずです。 やったね。

別の方法: regedit.exeで目的のキー情報を見付けられない場合

上記の方法ではregedit.exeでエクスポートしましたが、Linuxのchntpwコマンドを使う方法もあります。 情報の抜き出し方が違うだけで、それ以外の手順は変わりません。

Linux側にchntpwをインストールする

まずはLinuxにchntpwをインストールします。 元々はWindowsのパスワードを変えるためのコマンドなので CHange NT PassWord という名前みたいです。 レジストリの閲覧機能があるので、これを使って情報を取り出します。

apt-get、yum、portageなど主要なパッケージマネージャで入るようです。

Windowsのストレージをマウントする

レジストリを見るために、まずはWindowsがインストールされたパーティションをLinuxにインストールします。

$ sudo mount /dev/sdb4 /mnt/win

デバイス名、マウント先はよしなに。 もしかしたら、NTFSをマウントするためのfuseのインストールが必要かもしれません。

レジストリを開いて目的のデバイスを探す

以下のコマンドでchntpwを起動します。

$ chntpw -e /mnt/win/Windows/System32/config/SYSTEM
chntpw version 1.00 140201, (c) Petter N Hagen
Hive </mnt/win/Windows/System32/config/SYSTEM> name (from header): <SYSTEM>
ROOT KEY at offset: 0x001020 * Subkey indexing type is: 686c <lh>
File size 20185088 [1340000] bytes, containing 4299 pages (+ 1 headerpage)
Used for data: 302800/19733216 blocks/bytes, unused: 125/129984 blocks/bytes.

Simple registry editor. ? for help.

>

この時点で、regedit.exeで言う HKEY_LOCAL_MACHINE\SYSTEM に相当する位置がカレントディレクトリになっています。 ここから lscd を使って HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\BTHPORT\Parameters\Keys\001bdcxxxxxx に相当する場所まで潜っていきます。

> cd ControlSet001\Services\BTHPORT\Parameters\Keys

(...)\Services\BTHPORT\Parameters\Keys> ls
Node has 2 subkeys and 0 values
  key name
  <001bdxxxxxxx>

(...)\BTHPORT\Parameters\Keys> cd 001bdxxxxxx

上記の 001bdxxxxxx がPCについているレシーバのアドレスです。

ここで ls を実行すると、以下のようにWindows側で接続されているデバイスの一覧が表示されます。

(...)\Parameters\Keys\001bdxxxxxxx> ls
Node has 2 subkeys and 4 values
  key name
  <d3bc56xxxxxx>
  <e834dbxxxxxx>
  size     type              value name             [value if type DWORD]
    16  3 REG_BINARY         <88d039xxxxxx>
    16  3 REG_BINARY         <cc988bxxxxxx>
    16  3 REG_BINARY         <MasterIRK>
    16  3 REG_BINARY         <e09f2axxxxxx>

上に表示されている d3bc56xxxxxxe834dbxxxxxx がBLEデバイスで、下に表示されている 88d039xxxxxxcc988bxxxxxx がBluetoothデバイスです。

必要な情報を探してLinux側に反映する

Bluetoothデバイス

Bluetoothの場合のやり方はかなり単純で、以下のように hex コマンドを使うと必要なLinkKeyを取り出せます。

(...)\Parameters\Keys\001bdxxxxxxx> hex cc988bxxxxxx
Value <cc988bxxxxxx> of type REG_BINARY (3), data length 16 [0x10]
:00000  84 10 10 XX XX XX XX XX XX XX XX XX XX 27 CE ED ................

この場合、 841010XXXXXXXXXXXXXXXXXXXX27CEED がLinkKeyになります。

なので、Linux側のinfoファイルの内容は以下のようになります。

[LinkKey]
Key=84EDXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Type=4
PINLength=0

BLEデバイス

BLEの場合はディレクトリになっているので、まずはその中に入ります。

(...)\BTHPORT\Parameters\Keys\001bdcxxxxxx> cd e834dbxxxxxx

(...)\Parameters\Keys\001bdcxxxxxx\e834dbxxxxxx> ls
Node has 0 subkeys and 9 values
  size     type              value name             [value if type DWORD]
    16  3 REG_BINARY         <LTK>
     4  4 REG_DWORD          <KeyLength>               16 [0x10]
     8  b REG_QWORD          <ERand>
     4  4 REG_DWORD          <EDIV>                  1234 [0x042d]
    16  3 REG_BINARY         <IRK>
     8  b REG_QWORD          <Address>
     4  4 REG_DWORD          <AddressType>              1 [0x1]
     4  4 REG_DWORD          <MasterIRKStatus>          1 [0x1]
     4  4 REG_DWORD          <AuthReq>                 45 [0x2d]

ここで、 LTK ERand EDIV の3つを取り出します。

EDIV については表示されている10進数の値(上記の例だと 1234)がそのまま使えます。

LTKERand は下記のように hex コマンドで取得します。

(...)\Parameters\Keys\001bdcxxxxxx\d3bc56xxxxxx> hex LTK
Value <LTK> of type REG_BINARY (3), data length 16 [0x10]
:00000  A8 EC B2 XX XX XX XX XX XX XX XX XX XX F4 AD 76 ................


(...)\Parameters\Keys\001bdcxxxxxx\d3bc56xxxxxx> hex ERand
Value <ERand> of type REG_QWORD (b), data length 8 [0x8]
:00000  23 00 00 00 00 00 00 EB                         ........

LTK はスペースを消せばそのまま使えるのですが、 ERandもう一個の方法のregedit.exeを使った場合と同じ加工が必要になります。

というわけで、この例だとLinux側のinfoファイルは以下のようになります。

[LongTermKey]
Key=A8ECB2XXXXXXXXXXXXXXXXXXXXF4AD76
Authenticated=0
EncSize=16
EDiv=1234
Rand=16933534598913064995

参考: