| nav | 例 | ||||
|---|---|---|---|---|---|
| group | SESAME API | ||||
| type |
|
||||
| title | 重要概覽 | ||||
| order | 0 |
- ブルートゥース通信のプログラミングは物理層にとても近く、物理世界に近いほど完璧な抽象プログラミングの概念から遠くなります。 これらのブルートゥース命令を処理することは実際にとても煩雑で、SesameSDK と呼ばれるものは、これらの複雑な操作を優れた実装にし、開源ライブラリとして包装して利用しやすくしています。
- この一連のブルートゥースプロトコルは、CANDY HOUSE の三世代のエンジニアたちが 10 年以上(2014〜2016、2017〜2019、2020 年〜現在)の努力と知識を結集した結果であり、その無料でのオープンソース化は"安物買いの銭失い"ではなく、逆に、私たちが完璧だと思うこの作品が「生きた」ブルートゥースプロトコルとして、その中心思想を世界的に伝播し発展させることができることを願っています。
- ブルートゥースの操作には主に二つの部分が関わっています: ブルートゥース広告(BLE Advertising)とブルートゥースデータ伝送(BLE Data Transmission)。以下ではこれらについて詳しく説明します。
- Bluetooth LE 4.0 advertising の長さは最大でも 31 Bytes です。
- 黄色い背景または黄色い背景のマークは CANDY HOUSE が定義したプロトコルを示しています。
- それ以外の白い背景部分はすべて Bluetooth SIG の規格に従った強制的な宣言である。
- https://www.bluetooth.com/specifications/assigned-numbers/
- CANDY HOUSE が Bluetooth SIG に登録した公式の識別子:
| 十進数 | 16 進数 | |
|---|---|---|
| BLE Service UUID | 64897 | 0xFD81 |
| Company ID | 1370 | 0x055A |
- ブルートゥース広告の 8th 9th Bytes は機種を表しています
| 8th 9th Bytes | 機種 |
|---|---|
| 0 | Sesame 3 |
| 1 | WiFi Module 2 |
| 2 | Sesame Bot 1 |
| 3 | Sesame Bike 1 |
| 4 | Sesame 4 |
| 5 | Sesame 5 |
| 6 | Sesame Bike 2 |
| 7 | Sesame 5 Pro |
| 8 | Open Sensor |
| 9 | Sesame Touch 1 Pro |
| 10 | Sesame Touch 1 |
| 11 | Ble Connector |
| 13 | Hub 3 |
| 14 | Sesame Remote |
| 15 | Remote Nano |
| 16 | Sesame 5 Usa |
| 17 | Sesame Bot 2 |
| 18 | F2 |
| 19 | F1 |
| 20 | Bluetooth半導体光リレー |
- ブルートゥースの広告内の 10 番目のバイトは 8 つのビットに分割され、各ビットはデバイスの各状態を表しています。
| 10番目のバイトの8ビット | デバイスの状態 | |
| 0 | 1 | |
| 1st bit | 登録されていません | 登録済み |
| 2nd bit | ||
| 3rd bit | ||
| 4th bit | ||
| 5th bit | ||
| 6th bit | ||
| 7th bit | ||
| 8th bit | ||
- 49.2 Advertising Channels
アクセサリは、各広告イベントで
すべての3つの広告チャンネル(37、38、39)で広告を行うべきです。Bluetooth 4.0 の仕様書をご覧ください。Volumn 6, Part B, 節 4.4.2.1. - https://developer.apple.com/accessories/Accessory-Design-Guidelines.pdf Release R21
- Sesame BLE adv を評価し、理解するために、nRF Connect app を使用することをお勧めします。
- https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp
- https://apps.apple.com/app/id1054362403
- TCP/IP モデルから着想を得て、私たちはブルートゥース通信を 4 つのレイヤーに分けています。
平文で記述された任意の形式の情報を暗号化し、分割してBluetooth GATTにより受信・送信します。これら 4 つの状態はそれぞれ、
Application Layer、Security Layer、Transport Layer、Gatt Bearer Layerと呼ばれています。- この情報の分割の方法により、無限の長さの任意の形式の情報を送信することができます。
- RESTful API を用いてデータベースにデータを POST(追加、削除、参照、編集)することに着想を得て、私たちは
BLE write without responseに基づいて 1 つのコマンドメソッドを包括しました。
・リクエスト Request - RESTful API の async/sync に着想を得て、私たちは
BLE notifyを 2 つのコールバックメソッドに包括しました。
・レスポンス Response
・パブリッシュ Publish レスポンスメソッドと BLE write without 'レスポンス' は同名ですが全く関係ありません。これにより混乱が生じることを私たちは深くお詫び申し上げます。- すべての
リクエストにはすぐに一度レスポンスが反応します。実行の結果を得るために実行時間が必要な情報はパブリッシュで反応します。
- リクエスト、レスポンス、パブリッシュメソッドを使用してブルートゥース情報を送受信する際、Sesame BLE Service(0xFD81) の 2 つの BLE Characteristics だけが使用されます。
- UUID 16860002-a5ae-9856-b6d3-dbb4c676993e の Characteristics は BLE commands を write without response するために使われます。
- UUID 16860003-a5ae-9856-b6d3-dbb4c676993e の Characteristics は BLE Notify の受信に使われます。
- 一つ目は パブリッシュ Publishで、セサミが主導してメッセージを携帯電話に送信します。
- 二つ目は リクエスト Requestで、携帯電話がセサミに仕事を頼み、セサミが結果をレスポンス Responseで応答します。
NotifyEnable = True のときに有効となり、セサミが主導して設定したメッセージをスマートフォンに送信します。例えば、「応答コマンド表」 の 81_ギミックの状態が挙げられます。
| バイトのインデックス | 0 | 1 | 2 ~ N |
|---|---|---|---|
| データ | タイプ | コマンド番号 | 携帯電話へのデータ |
携帯電話からセサミにコマンドをリクエストし、セサミが確認やインタプリタの応答を行います。
| バイトのインデックス | 0 | 1 | 2 ~ N |
|---|---|---|---|
| データ | タイプ | コマンド番号 | 携帯電話からのデータ |
セサミがそのリクエストを受領したことを確認し、コールバック情報を発行します。
| バイトのインデックス | 0 | 1 | 2 | 3 | 4 ~ N |
|---|---|---|---|---|---|
| データ | タイプ | コマンド番号 | フラグ | ステータス | 携帯電話へのデータ |
インタプリタが成功のステータスである場合、フラグ=0x00, ステータス=0x00。
| バイトのインデックス | データ | 説明 |
|---|---|---|
| 0 | 0x02 | タイプ |
| 1 | コマンド番号 | コマンド番号 |
| 2 | 0x00 | フラグ |
| 3 | 0x00 | 成功ステータス |
| 4 ~ N | セサミからのデータ | セサミからのデータ |
インタプリタがエラーのステータスである場合、フラグ=0x80, ステータス=エラーコード。
| バイトのインデックス | データ | 説明 | | :------------------: | :----------------: | :----------------: | ---- | | 0 | 0x02 | タイプ | | 1 | コマンド番号 | コマンド番号 | | 2 | 0x80 | フラグ | | 3 | エラーコード | 失敗用のステータス | | 4 ~ N | セサミからのデータ | セサミからのデータ | ```c |
typedef struct { uint8_t type; /// プッシュタイプ:publish uint8_t item_code; /// コマンドの item_code union ss5_payload payload; /// モバイルに送信するデータ } ss5_publish;
### 2. リクエスト【携帯端末->Sesame】
- `Request` は携帯が Sesame に処理を要請し、Sesame は受信状態を返答します。例えば「命令コード対照表」82_ロックのように。
- 混乱を避けるために、同期シリアル送信をサポートする順序付けられたキューが必要です。BLE は下位機として、特性値`Characteristics`を読み書きします。読み書きの操作はプロトコルの内容を送信する方法です。
| バイトインデックス | 0 | 1 ~ N |
| :------: | :-------: | :----------------: |
| データ | item_code | ペイロード |
| 説明 | コマンド番号 | Sesameに送信されるデータ |
```c
typedef struct {
uint8_t item_code; /// コマンド(item_codeに詳しい)
union ss5_payload payload; /// Sesame5に送信されるデータ
} ss5_request;
typedef enum {
ITEM_CODE_NONE = 0,
ITEM_CODE_REGISTRATION = 1,
ITEM_CODE_LOGIN = 2,
ITEM_CODE_HISTORY = 4,
ITEM_CODE_VERSION_DETAIL = 5,
ITEM_CODE_TIME = 8,
ITEM_CODE_AUTOLOCK = 11,
ITEM_CODE_INITIAL = 14,
ITEM_CODE_IRER = 15,
ITEM_CODE_MAGNET = 17,
ITEM_CODE_MECH_SETTING = 80,
ITEM_CODE_MECH_STATUS = 81,
ITEM_CODE_LOCK = 82,
ITEM_CODE_UNLOCK = 83,
SSM3_ITEM_RESET = 104
} item_code_e;| バイトインデックス | 0 | 1 | 2 | 3 ~ N |
|---|---|---|---|---|
| データ | type | item_code | res | payload |
| 説明 | プッシュの種類 | コマンド番号 | コマンド処理状態 | モバイルに送信するデータ |
typedef struct {
uint8_t type; /// プッシュタイプ:Response
uint8_t item_code; /// コマンド(item_code)
uint8_t res; /// コマンド処理状態 (常にsuccessを返す)
union ss5_payload payload; /// モバイルに送信するデータ
} ss5_response;typedef enum {
CMD_RESULT_SUCCESS = 0, /// 手機からのコマンドに対してSesame5は常に成功を返す
CMD_RESULT_INVALID_ACTION = 9, /// 既に登録されているのに登録コマンドが下された
} cmd_result_e;0x07のマークは通常のレスポンスresponse、0x08はプッシュpublishを示します。
typedef enum {
OP_CODE_RESPONSE = 0x07,
OP_CODE_PUBLISH = 0x08,
} op_code_e;| バイトインデックス | 0 | 1 ~ N | N+ |
|---|---|---|---|
| データ | item_code | payload | その他のデータ |
| 説明 | コマンド番号 | Sesame に送信するデータ | その他のデータ |
| 登録 | 1 | 1-64【publicKeyA】 | 65-68【timestamp】 |
| ログイン | 2 | 1-4【ccmkeyA】 | |
| 履歴 | 4 | 1【is peek】 | ispeek=true 削除しない |
| バージョン | 5 | none | |
| 時間更新 | 8 | 1-4【timestamp】 | |
| 自動ロック | 11 | 1-2【秒数】 | |
| 角度調整 | 17 | none | |
| 機器設定 | 80 | 1-5【autolock+開閉角度】 | |
| 機器状態 | 81 | none | No Response |
| ロック | 82 | 1-7【履歴タグ】 | |
| アンロック | 83 | 1-7【履歴タグ】 | |
| 開ける | 90 | none | |
| 閉じる | 91 | none | |
| 自動ロック | 92 | none | |
| リセット | 104 | none | |
| Sesame 追加「touch」 | 101 | 1-16【デバイス名】 | 17-32【secretKey】 |
| Sesame 削除「touch」 | 103 | 1-16【デバイス名】 | |
| カード更新「touch」 | 107 | 0【カード ID 長】1-id_len【カード ID】 | CardIDLen+1【カード名長】等 |
| カード削除「touch」 | 108 | 1-16【カード ID】 | |
| カード取得「touch」 | 113 | none | |
| カードモード | 114 | none | |
| フィンガープリント更新 | 115 | 0【FingerIDLen】 | 1-Len【FingerID】】 |
| フィンガープリント削除 | 116 | 1【FingerID】 | |
| フィンガープリント取得 | 117 | none | |
| フィンガープリントモード | 121 | none | |
| フィンガープリントモード設定 | 122 | none | |
| パスワード更新 | 123 | 0【PwIDlen】 | 1-pwLen【パスワード】】 |
| パスワード削除 | 124 | none | |
| パスワード取得 | 125 | none | |
| パスワードモード取得 | 129 | none | |
| パスワードモード設定 | 130 | 0 認証 1 新規記入 |
| バイトインデックス | 0 | 1 | 2 | 3 ~ N | N+ | N++ |
|---|---|---|---|---|---|---|
| データ | type | item_code | res | payload | payload | payload |
| 説明 | プッシュの種類 | コマンド番号 | コマンド処理状態 | モバイルに送信するデータ 1 | モバイルに送信するデータ 2 | モバイルに送信するデータ 3 |
| 初期化 | 8 | 14 | 0 | 0-3【random code】 | none | |
| 登録 | 7 | 1 | 0 | 0-6【mechStatus】 | 7-12【mechSetting】】 | 13~76【publicKeyS】】 |
| ログイン | 7 | 2 | 0 | 0-3【timestamp】 | ||
| 履歴 | 7 | 4 | 0 | 0-3【id】】 | 4【type】 | 5-8【timestamp】 |
| バージョン | 7 | 5 | 0 | 0-11【version】 | ||
| 機器設定 | 8 | 80 | 0 | 0-1【lock】 | 2-3【unlock】】 | 4-5【autolock_second】 |
| 機器状態 | 8 | 81 | 0 | 0-3【random code】 | ||
| Sesame キーのプッシュ | 8 | 102 | 0 | 0-21【ssm0-name】 | 22【ssm0-status】 | 23-44【ssm1_name】等 |
| Sesame キーの取得 | 7 | 109 | 0 | 0-3【CardID】 | ||
| フィンガープリントの取得 | 7 | 117 | 0 | 0-3【FingerID】 | ||
| フィンガープリントモードの取得 | 8 | 121 | 0 | 0-3【random code】 |
セキュリティ層 Security Layer は 携帯の Bluetooth と Sesame5 間のデータパススルー実装の暗号化転送プロトコルです。
Sesame デバイスの Bluetooth を探し、接続して特性値が0xFD81のnotifyを開くと、Sesame デバイスは主動的にpublish送信し、4 バイトのランダムコード/セッショントークンを送信します。(詳細は14_初期化参照)
登録時、アプリと Sesame5 は両方とも secp256r1 を使用して public key と private key を生成し、共有して Device secret を生成します。
publicKey: 64 バイトprivateKey: 32 バイト
シークレット : 32 バイト
-
- sesame5 は
secp256r1を使用してpublicKeySとprivateKeySを生成する
- sesame5 は
-
- アプリは
secp256r1を使用することでpublicKeyAとprivateKeyAを生成する
- アプリは
-
- 両方の
publicKeyを交換する
- 両方の
-
- sesame5 は
privateKeySとpublicKeyAを使用してsecret Sを生成する
- sesame5 は
-
- アプリは
privateKeyAとpublicKeySを使用してsecret Aを生成する
- アプリは
-
secret Sはsecret Aと等しい
-
- 両方のシークレットの上位 16 バイトをキーとして使用する
デバイスシークレット。
- 両方のシークレットの上位 16 バイトをキーとして使用する
-
- ログイン認証(詳細は:
2_ログイン)
- ログイン認証(詳細は:
-
aes_ccmを使用してデータを暗号化する際、キーとしてトークンを使用。
Device secretを使用して、AES_CMAC アルゴリズムを使用して 4Bytes のランダムコードに署名すると、Tokenを生成できます。
device_secret:16 Bytes
random_code:4 Bytes
Token:16 Bytes
AES_CMAC(key:device_secret, input:random_code, input_size:4, output:Token);-
- count:8 バイト、起動時に 0 に初期化し、暗号化を行うたびに count が 1 増える
-
- nouse:1 バイト、デフォルト値は 0
-
- random_code:4 バイト、デバイスへの接続後に
random_codeを更新
- random_code:4 バイト、デバイスへの接続後に
typedef struct {
int64_t count;///8 byte
uint8_t nouse;///1 byte = 0
uint8_t random_code[4];///4 byte
} CCM_IV;入力:
-
- key:暗号キー、AES-CCM 操作に使用
-
- iv:初期化ベクトル(CCM_IV)
-
- iv length:AES-CCM は IV として 13 バイト(104 ビット)を使用
-
- add:追加認証データ
-
- add length:追加認証データの長さ
-
- input:暗号化する入力データ
-
- input length:暗号化するデータの長さ
-
- tag length:認証タグ(MAC)を保存するバッファとタグの長さ
出力:
-
- tag:ccm_tag
-
- output:ccm_ecrrypt_data d 暗号化したデータ
aes_ccm_encrypt_and_tag(
key:ccm_key,
iv:CCM_IV,
iv_length:13,
add:0x00,
add_length:1,
input:p_data,
add_length:length,
output:ccm_ecrrypt_data,
tag:ccm_tag,
tag_length:4
);
CCM_IV.count ++;AES は対称暗号化アルゴリズムであり、その方法名はaes_ccm_auth_decryptで、パラメータは暗号化と対応しています。
aes_ccm_auth_decrypt(
key:ccm_key,
iv:CCM_IV,
iv_length:13,
add:0x00,
add_length:1,
input:ccm_ecrrypt_data,
add_length:length,
output:ans_data,
tag:ccm_tag, tag_length:4
);
CCM_IV.count ++;:::info{title=注意} データの暗号化と復号化で使用する CCM_IV には、暗号化の回数を記録する count が含まれています。暗号化と復号化のエンドで count が一致しないと、復号化が失敗します。 :::
セグメントレイヤは 2 つの作業をします。
-
- Sesame5 とスマホの通信時、パケットの最大長は 20Bytes に制限されており、長いメッセージを送るにはデータを分割し、受信端では再組み立てる必要があります。
-
- 送信データが暗号化されているかどうかを示すマーク、3 種類の長さの平文暗号文パケットの方式を示す。
セグメントレイヤはパケットヘッダの中のマークでこのパケットが分割されているか暗号化されているかを示し、受信端はこのマークに従って対応する処理を行い、パケットを元のデータに復元します。以下はセグメントレイヤのマークの対照表です。
セグメントレイヤのマークは 8 ビットあり、うちビット 7 ~ 1 は終了パケットであるかデータが暗号化されているかを示し、ビット 0 はそのパケットが一つのデータの開始パケットであるかを示します。
| ビット 7 ~ 1 | 終了 | ビット | 開始 |
|---|---|---|---|
| b0000000 | 終了しない | 0 | 開始ではない |
| b0000000 | 終了しない | 1 | 開始 |
| b0000001 | 平文終了 | 0 | 開始ではない |
| b0000001 | 平文終了 | 1 | 開始 |
| b0000010 | 暗号化終了 | 0 | 開始ではない |
| b0000010 | 暗号化終了 | 1 | 開始 |
0 はそのパケットが開始パケットでも終了パケットでもないことを示す
1 はそのパケットが終了パケットではない開始パケットであることを示す
2 はそのパケットが開始パケットではなく、終了パケットであり、組み合わせたデータが平文であることを示す
3 はそのパケットが開始パケットであり終了パケットであることを示す。そしてそのデータは平文であり、データは分割されておらず、組み合わせる必要はありません。
4 はそのパケットが開始パケットではないが、終了パケットであり、組み合わせたデータが暗号文であることを示す
5 はそのパケットが開始パケットであり終了パケットであることを示す。そしてそのデータは暗号文であり、データは分割されておらず、組み合わせる必要はありません。
| マーク | 開始 | 終了 |
|---|---|---|
| 0x00 | 開始ではない | 終了しない |
| 0x01 | 開始 | 終了しない |
| 0x02 | 開始ではない | 平文終了 |
| 0x03 | 開始 | 平文終了 |
| 0x04 | 開始ではない | 暗号化終了 |
| 0x05 | 開始 | 暗号化終了 |
あるパケットが 4 バイトのデータを格納でき、4 バイトを超えると分割が必要であるとします(実際は 20 バイトです)。
データ: A A B B
送信:
1. データを受け取る : A A B B
2. データ : A A B B
3. パケットヘッダを付加 : 3 A A B B
受信:
4. 受け取る : 3 A A B B
5. パケットヘッダを除去し、元のデータを復元 : A A B B
データ: A A A A B B B B
送信:
1. データを受け取る : A A A A B B B B
2. データを分割 : A A A A
3. パケットヘッダを付加 : 1 A A A A
4. データを分割 : B B B B
5. パケットヘッダを付加 : 2 B B B B
受信:
6. 受け取る : 1 A A A A
7. データがまだ終わっていないと判断し、次のパケットを待つ
8. 受け取る : 2 B B B B
9. データの受信が完了し、パケットを組み合わせる
10. パケットヘッダを除去し、元のデータを復元 : A A A A B B B B
データ: A A A A B B B B C C C C
送信:
1. データを受け取る: A A A A B B B B C C C C
2. データを分割: A A A A
3. パケットヘッダを付加: 1 A A A A
4. データを分割: B B B B
5. パケットヘッダを付加: 0 B B B B
6. データを分割: C C C C
7. パケットヘッダを付加: 2 C C C C
受信:
8. 受け取る: 1 A A A A
9. データがまだ終わっていないと判断し、次のパケットを待つ
10. 受け取る: 0 B B B B
11. データがまだ終わっていないと判断し、次のパケットを待つ
12. 受け取る: 2 C C C C
13. データの受信が完了し、パケットを組み合わせる
14. パケットヘッダを除去し、元のデータを復元 : A A A A B B B B C C C C
データ: A A B B
暗号化後のデータ: X X Y Y
送信:
1. データを受け取る: A A B B
2. データを暗号化: X X Y Y
3. パケットヘッダを付加: 5 X X Y Y
受信:
4. 受け取る: 5 X X Y Y
5. パケットヘッダを除去し、元のデータを復元: X X Y Y
6. 復号化: A A B B
データ: A A A A B B B B
暗号化後のデータ: X X X X Y Y Y Y
送信:
1. データを受け取る: A A A A B B B B
2. データを暗号化: X X X X Y Y Y Y
3. データを分割: X X X X
4. パケットヘッダを付加: 1 X X X X
5. データを分割: Y Y Y Y
6. パケットヘッダを付加: 4 Y Y Y Y
受信:
7. 受け取る: 1 X X X X
8. データがまだ終わっていないと判断し、次のパケットを待つ
9. 受け取る: 4 Y Y Y Y
10. データの受信が完了し、パケットを組み合わせる
11. パケットヘッダを除去し、元のデータを復元: X X X X Y Y Y Y
12. データを復号化: A A A A B B B B
データ: A A A A B B B B C C C C
暗号化後のデータ: X X X X Y Y Y Y Z Z Z Z
送信:
1. データを受け取る: A A A A B B B B C C C C
2. データを暗号化: X X X X Y Y Y Y Z Z Z Z
3. データを分割: X X X X
4. パケットヘッダを付加: 1 X X X X
5. データを分割: Y Y Y Y
6. パケットヘッダを付加: 0 Y Y Y Y
7. データを分割: Z Z Z Z
8. パケットヘッダを付加: 4 Z Z Z Z
受信:
9. 受け取る: 1 X X X X
10. データがまだ終わっていないと判断し、次のパケットを待つ
11. 受け取る: 0 Y Y Y Y
12. データがまだ終わっていないと判断し、次のパケットを待つ
13. 受け取る: 4 Z Z Z Z
14. データの受信が完了し、パケットを組み合わせる
15. パケットヘッダを除去し、元のデータを復元: X X X X Y Y Y Y Z Z Z Z
16. データを復号化: A A A A B B B B C C C C
:::info{title=注意}
データは AES_CCM で暗号化されると、暗号文だけでなく、復号化に使用する 4 バイトの ccm_tag も生成されます。暗号文と ccm_tag は双方とも受信端に送信する必要があり、そのため暗号化後のデータは 4 バイト増加します。詳細は security_Layer をご覧ください。
:::







