エンドツーエンド暗号化
bleRPCは、4ステップの鍵交換ハンドシェイクとAES-128-GCMセッション暗号化によるオプションのエンドツーエンド暗号化をサポートしています。暗号化は接続セットアップ時にネゴシエーションされ、アプリケーション層に対して透過的です。
概要
| コンポーネント | アルゴリズム |
|---|---|
| 鍵合意 | X25519 ECDH |
| 認証 | Ed25519署名 |
| 鍵導出 | HKDF-SHA256 |
| セッション暗号化 | AES-128-GCM |
| リプレイ防止 | 単調増加カウンター |
ケイパビリティネゴシエーション
暗号化は、ペリフェラルのCAPABILITIESレスポンスでアドバタイズされます。6バイトのペイロードにはflagsフィールドが含まれます:
flags & 0x01がセットされている場合、ペリフェラルは暗号化をサポートしており、セントラルは鍵交換ハンドシェイクを開始する必要があります。
鍵交換ハンドシェイク
ハンドシェイクは4つのステップを使用し、各ステップはKEY_EXCHANGE制御コンテナ(control_cmd=0x6)で運ばれます。各ペイロードには1バイトのステップ識別子がプレフィックスとして付きます。
sequenceDiagram
participant C as Central
participant P as Peripheral
Note over C: Generate ephemeral X25519 keypair
C->>P: Step 1: [step:1] + central_x25519_pubkey (33 bytes)
Note over P: Generate ephemeral X25519 keypair
Note over P: Compute shared_secret = X25519(priv, central_pub)
Note over P: Derive session_key = HKDF(shared_secret)
Note over P: Sign(ed25519_privkey, central_pub || peripheral_pub)
P->>C: Step 2: [step:1] + x25519_pub + signature + ed25519_pub (129 bytes)
Note over C: Compute shared_secret = X25519(priv, peripheral_pub)
Note over C: Derive session_key = HKDF(shared_secret)
Note over C: Verify signature (TOFU for first connection)
Note over C: Generate confirmation = encrypt(random, session_key)
C->>P: Step 3: [step:1] + encrypted_confirmation (45 bytes)
Note over P: Decrypt confirmation to verify session_key match
Note over P: Generate confirmation = encrypt(random, session_key)
P->>C: Step 4: [step:1] + encrypted_confirmation (45 bytes)
Note over C: Decrypt confirmation to verify session_key match
Note over C,P: Session key established - all subsequent data is encrypted
ステップ1: Central → Peripheral (33バイト)
セントラルがエフェメラルなX25519鍵ペアを生成し、公開鍵を送信
ステップ2: Peripheral → Central (129バイト)
ペリフェラルが独自のエフェメラルなX25519鍵ペアを生成し、共有シークレットを計算し、セッション鍵を導出し、長期Ed25519鍵で両方の公開鍵に署名
署名はcentral_x25519_pubkey || peripheral_x25519_pubkey(64バイト)を対象とし、中間者攻撃を防ぐために両者のエフェメラル鍵をバインド
ステップ3: Central → Peripheral (45バイト)
セントラルが署名を検証し、共有シークレットとセッション鍵を計算した後、正しいセッション鍵を保持していることを証明するために暗号化された確認を送信
平文は16バイトのランダムデータで、導出されたセッション鍵とランダムな12バイトのnonceを使用してAES-128-GCMで暗号化
ステップ4: Peripheral → Central (45バイト)
ペリフェラルがステップ3を復号してセッション鍵を検証し、独自の暗号化された確認を送信
セントラルがステップ4の復号に成功すると、ハンドシェイクが完了し、両者が同じセッション鍵を共有
鍵導出
セッション鍵はX25519共有シークレットからHKDF-SHA256を使用して導出されます:
shared_secret = X25519(my_privkey, peer_pubkey) # 32 bytes
session_key = HKDF-SHA256(
salt = central_x25519_pubkey || peripheral_x25519_pubkey, # 64 bytes
ikm = shared_secret,
info = b"blerpc-session-key",
len = 16 # AES-128 key
)
セントラルとペリフェラルの両方が、両者のX25519公開鍵の連結をソルトとして使用して同じ導出を行い、同一の16バイトセッション鍵に到達
セッション暗号化
ハンドシェイク後、すべてのコマンドペイロードはコンテナ分割前に暗号化されます:
暗号化ペイロードフォーマット
暗号化プロセス
- CommandPacketをシリアライズ(タイプ + 名前 + protobufデータ)
- TXカウンターをインクリメント
- 12バイトのnonceを構築:
[counter_LE:4][direction:1][0x00:7]—directionはCentral→Peripheralの場合0、Peripheral→Centralの場合1 - AES-128-GCMで暗号化:
ciphertext, tag = AES-GCM(session_key, nonce, plaintext) - カウンターをプレフィックス:
[counter:4][ciphertext:N][tag:16] - 通常通りコンテナに分割
復号プロセス
- コンテナを完全なペイロードに再組み立て
- カウンター(先頭4バイト)、暗号文、タグ(末尾16バイト)を抽出
- カウンターが最後に受信したカウンターより大きいことを検証(リプレイ検出)
- nonceを構築しAES-128-GCMで復号
- 復号されたデータをCommandPacketとしてパース
リプレイ検出
各方向は独立した単調増加カウンターを維持します:
- TXカウンター: 暗号化の前にインクリメント。0から開始
- RXカウンター: 最後に受信したカウンターを追跡。最後に受信したカウンター以下のカウンターを持つメッセージは拒否されます
これにより、攻撃者が過去にキャプチャした暗号化パケットを再送信するリプレイ攻撃を防止
信頼モデル
Trust On First Use (TOFU)
最初の接続時、セントラルはペリフェラルのEd25519公開鍵を受け入れて保存します。以降の接続では、セントラルはペリフェラルが同じ鍵を提示するかを検証します。鍵が変更された場合、接続は拒否される(またはユーザーに確認を求める)
これはSSHのknown_hostsメカニズムに類似しています。PKIや認証局を必要とせずに、最初の接続後の中間者攻撃に対する保護を提供
プラットフォーム実装
| プラットフォーム | 暗号ライブラリ | 鍵交換 | セッション暗号化 |
|---|---|---|---|
| Python | cryptography | CentralKeyExchange / PeripheralKeyExchange | BlerpcCryptoSession |
| Kotlin (Android) | Java Crypto | CentralKeyExchange | BlerpcCryptoSession |
| Swift (iOS) | CryptoKit | CentralKeyExchange | BlerpcCryptoSession |
| Dart (Flutter) | cryptography | CentralKeyExchange | BlerpcCryptoSession |
| C (Zephyr) | PSA Crypto (mbedTLS) | blerpc_central_kx_* / blerpc_peripheral_kx_* | blerpc_crypto_session |
設定
暗号化の有効化
暗号化はペリフェラル側で有効にします。有効にすると、ペリフェラルはケイパビリティで暗号化サポートをアドバタイズし、セントラルが鍵交換を開始することを期待
Zephyrファームウェア
# prj.conf
CONFIG_BLERPC_ENCRYPTION=y
Python Peripheral
# server.py - Ed25519署名鍵が提供されると暗号化が有効になります。
# X25519鍵ペアは接続ごとにエフェメラルに生成されます。
peripheral = BlerpcPeripheral(
ed25519_private_key_hex="...",
)