カテゴリー: Tech

BLEのペアリングをWiresharkでキャプチャしながら学ぶ

はじめに

BLEはペアリングすることなく通信することができますが、その状態ではデバイスの付近にいれば誰でも通信できてしまいます。そのため、製品化する場合には、ほとんどのケースでペアリングを実装する必要があります。
例えば、スマートウォッチでは、初回起動時にスマホとのペアリングをすることで、他者からアクセスできないようにしています。

BLEの開発をしていると、ペアリングに関する部分は低レイヤーなので、アプリケーションレベルで気にすることは少ないのですが、今回は実際にどのようにペアリングが行われているのかを掘り下げてみたいと思い、Wiresharkでキャプチャーしながら学ぶことにしました。

ペアリングとボンディング

Bluetoothのデバイスを使用するとき、ユーザはペアリングをして、その後はペアリング作業なしに接続できるようにしたいと思います。このようにするためには、Bluetoothの仕様上、「ボンディング」が必要になります。
ペアリングとは、デバイスとの暗号化通信を行うための鍵を交換すること自体を言います。そして、ボンディングとは、ペアリングを実施し、生成された鍵を保存することで、以降の暗号化通信ができるようにしておくことを言います。
今回は、ボンディングも行うケースとなります。

暗号化はキャラクタリスティック単位

BLEでは、暗号化はキャラクタリスティックの属性として設定します。そのため、1つのサービス内で、公開されたキャラクタリスティックと、暗号化されたキャラクタリスティックを混在させることができます。

ペアリングしていない状態で暗号化されたキャラクタリスティックにアクセスすると、 Error Code: Insufficient Encryption (0x0f)というエラーがレスポンスされます。
実際にWiresharkで見てみるとこのようになります。

ちなみに、今回は次のような構成になっています。

デバイス ペアリング時の役割 BLEの役割 Wireshark上の表示 IO Capability
iPhone イニシエータ セントラル MACアドレス(ぼかしています) DisplayKeyboard
Raspberry Pi レスポンダ ペリフェラル localhost() DisplayOnly

Raspberry Pi側は、次のような構成になっています。

  • BlueZ
    Bluetooth通信の窓口となっているモジュールで、デーモンとして動いています。bluezと各種ソフトウェアは、DBusを経由して対話します。
    実際のペアリングや、ペアリングで交換した鍵の保存などもBlueZが行っています。
  • bt-agent
    IO Capabilityの指定や、ペアリング時に生成されたPasskeyの表示などを行います。起動させると、BlueZ側にAgentとして登録されることで動作します。BlueZとの対話はDBusを使用します。
  • キャラクタリスティック用のプログラム
    暗号化属性を与えたキャラクタリスティックのプログラムを用意します。BlueZとはDBusで対話するため、Pythonなどで実装可能で、キャラクタリスティックのflagsに encrypt-readもしくは encrypt-writeを指定します。

ペアリングの流れ

BLEでは、暗号化通信に使用する暗号鍵をペアリングで交換し、以降の通信で使います。この暗号鍵のことをLTK(Long Term Key)と呼び、ここからはLTKを交換するためのペアリングの流れについて説明します。
ペアリングは次の図の流れで行われます。

ペアリングリクエストを送った方がイニシエータとなり、その相手がレスポンダとなります。
イニシエータが暗号化されたキャラクタリスティックにアクセスすると、先ほどの通りエラーがレスポンスされますが、その際にセキュリティリクエストもレスポンスされます。
それを受けて、イニシエータ側はペアリングれリクエストを行います。

BLEペアリングには、次の2種類があります。

  • LE legacy pairing
  • LE Secure Connections

この2つの違いは、LTK(LongTerm Key)を無線通信上で交換するか否かになります。Legacy Pairingでは最初にSTK(Short Term Key)を生成し、STKによる暗号化通信でLTKを無線通信で交換します。
このため、legacy pairingではLTKが盗聴される可能性があります。しかし、Secure Connectionsであれば、LTKはPasskey認証などによって各デバイス上で生成されるため、無線通信上で交換する必要がなく、安全です。
今回は、Secure Connectionsの流れを見ていきたいと思います。

セキュリティリクエスト

一番初めにレスポンダからセキュリティの要求が行われます。この通信は任意ですが、ペアリングリクエストと似たようなパラメータ構成となっています。

ペアリングリクエスト・レスポンス

イニシエータからレスポンダにペアリングリクエストが送られることで、ペアリングが開始します。ここで、IO Capabilityをリクエスト時に送信し、レスポンダ側も自身のIO Capabilityをレスポンスします。
IO Capabilityとは、デバイスのもつ入手出力の特性のことで、主に次のように設定します。

  • DisplayOnly もしくは DisplayYesNo: Passkeyの表示によるペアリング
    例: スマートウォッチなど
  • KeyboardOnly  もしくは KeyboardDisplay: キーボード入力によるペアリング
    例: スマホ、キーボードなど
  • NoInputNoOutput
    例: マウスなど、ディスプレイもキーボードも持たないデバイス

今回、イニシエータのiPhoneはKeyboard Display、レスポンダのRaspberry PiはDisplay Onlyとしています。この場合、iPhone側には次のようなPasskey入力画面が表示され、Raspberry Pi側に出力された6桁のPasskeyを入力します。

Raspberry Pi側では、bt-agentを立ち上げている状態にしておくと、ターミナルにPasskeyが出力されるので、これをiPhoneで入力します。
bt-agentは先ほど説明した通り、デーモンで動いているBlueZと対話し、IO Capabilityの指定と、生成されたPasskeyの表示を行います。
Passkeyの生成や、ペアリング自体はBlueZ側が行うので、あくまでbt-agentはDBusを経由してBlueZと対話するのみとなります。

$ bt-agent -c DisplayOnly
Agent registered
Default agent requested
Device: iPhone (00:00:00:00:00:00)
Passkey: 186754, entered: 0

Wiresharkで見てみると、次のようになります。
まずは、ペアリングリクエストです。
ここで、 Bonding Flagsがありますが、ここを 0x1(Bonding)にすると、この後生成するLTK(Long Term Key)をお互いに保存しておき、次回以降ペアリングが不要となります。
レスポンスは次のようになります。
レスポンダであるRaspberry PiのIO Capabilityは、Dsiplay Onlyに設定しました。この組み合わせの場合は、先ほど説明したように、Raspberry Piで表示されたPasskeyを、iPhoneに入力することで認証します。
お互いのIO Capabilityの組み合わせによって、認証時のユーザによる作業が決定します。

Passkeyの検証

正しくPasskeyが入力されたかを検証します。検証用の公開鍵の交換、Passkeyの検証、DH Key検証の3ステップで行われます。
先ほどの通り、PasskeyからLTKを生成できるので、Passkeyの検証を通過すれば、LTKが有効であることが確認され、ペアリングが完了します。

生成されたLTKは、Raspberry PiではBlueZに保存されます。以下のコマンドを使うと、実際にペアリングされたデバイスを確認することができます。

$ bluetoothctl
Agent registered
[bluetooth]# devices
Device 00:00:00:00:00:00 My iPhone

iPhoneでは、設定画面のBluetoothでペアリング済みデバイスとして表示されます。

キャラクタリスティックとの暗号化通信

ペアリングが完了すると、暗号化されたキャラクタリスティックを読み込むことができました。
一度交換したLTKは、いずれかのデバイスがペアリング解除を行うまで使用できます。

おまけ

Raspberry PiでWiresharkを使うと、Bluetoothのパケットをキャプチャーすることができます。導入は非常に簡単です。

$ sudo apt install wireshark
$ sudo wireshark

これでWiresharkが起動するので、 bluetooth0を選択すると、Bluetoothの通信をキャプチャすることができます。

BLEはプロトコルスタックで、実際には様々なプロトコルの通信が行われているため、プロトコルによるフィルタリングは便利でした。
例えば、SMPプロトコルのみ表示するには、 btsmpで絞り込みます。また、ATTプロトコルを絞り込むには btattを指定します。
その他にも、Bluetoothのフィルター条件がたくさんあるので、右側の「書式…」ボタンを押せば、他のフィルターも探すことができます。

さいごに

次回はBlueZやbluetoothctlの仕組みについて深堀していきたいと思います!

おすすめ書籍

カイザー

シェア
執筆者:
カイザー
タグ: BLEBluetooth

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

3週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

1か月 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前