Bitcoinネットワークのピア間の通信を暗号化するBIP-151の提案が出てたので見てみる。
bips/bip-0151.mediawiki at master · bitcoin/bips · GitHub
動機
Bitcoinネットワークでは現在ピア間の通信を暗号化していないため、(トラフィックの操作のような)セキュリティ上の課題やBitcoinユーザの監視/分析を可能にしている。Bitcoinの信頼モデルの性質上は取るに足らない問題だが、SPVノードの場合、重大なプライバシーへの影響*1がある。
ピアのトラフィックを暗号化すると特定のユーザをターゲットにした分析は非常に難しくなる。現在はネットワークプロバイダもしくは中間者攻撃を行う側にとっては、Bitcoinユーザとそのユーザのコントール下にあるアドレスやキーを識別するのは簡単なことである。ついさっき作成しブロードキャストしたトランザクションに含まれるBitcoinの量や支払先の情報は、ネットワークプロバイダにとっては明白な情報である。
またこのBIPでは、ピア間の通信で(TCP/IPノードをインターセプトしてコマンドをブロックするような)データ操作を識別する方法を説明する。
暗号化されたメッセージの特性(サイズや送信間隔など)からP2Pの通信の種類を分析することはまだ可能である。
ピア間の通信を暗号化するのは、VPNやtor、stunnel、stunnelや深いOSIレベルの暗号化の仕組みで既に可能だが、ほとんどの仕組みはSPVや他のDHCP/NAT環境下では 、どのようにセキュアなチャネルをセットアップすればいいかのノウハウが必要で実用的ではない。
仕様
暗号化をサポートするピアは全てのピアから暗号化の要求を受け入れる必要がある。
双方向通信のための独立したECDHネゴシエーションが必要なため、2つの対称鍵暗号を使用する。
各ピアは双方向のECDHのネゴシエーションが成功した場合のみ、暗号化したメッセージを送信する必要がある。
暗号化の初期化処理は応答するピアに他のメッセージを送る前に処理する必要がある。
対称暗号の暗号鍵
対称暗号方式の暗号鍵は、一時的な公開鍵を共有することによりECDHで計算される。互いのピアでECDHのsecretが計算されると、対称暗号の暗号鍵を↓で計算する必要がある。
HMAC_SHA512(key=ecdh_secret|cipher-type,msg="encryption key")
K_1はHMAC_SHA512ハッシュの左32バイトである必要がある。
K_2はHMAC_SHA512ハッシュの右32バイトである必要がある。
弱い暗号を狙った攻撃を回避するため、対称暗号鍵に暗号タイプを含めるのが重要。
Session ID
各ピアは双方、↓を使って256bitのsession-idを計算する必要がある。
HMAC_SHA256(key=ecdh_secret,msg="session id")
session-idは、暗号化セッションをリンクするためのidentityのチェックに使われる。
encinit メッセージタイプ
暗号化通信を要求するには、要求側のピアがEC ephemeral-session-keypairを生成し応答するピアにencinitメッセージを送りencackメッセージを待つ。応答するノードは、反対方向の通信に対して同じencinit/encackの反応をする必要がある。
フィールドサイズ | 定義 | データタイプ | コメント |
---|---|---|---|
33バイト | ephemeral-pubkey | comp.-pubkey | 要求側のピアからのセッションの公開鍵 |
1バイト | symmetric key cipher type | int8 | 使用する対称鍵暗号のタイプ |
利用可能な対称鍵暗号のタイプは↓
Number | 対称鍵暗号のタイプ |
---|---|
0 | chacha20-poly1305@openssh.com |
ChaCha20-Poly1305 Cipher Suite
ChaCha20はDaniel Bernsteinによって設計されたストリーム暗号*2。128ビット長のデータ、126ビット長もしくは256ビット長の鍵と、64ビットのnonce、64ビットのカウンタを操作して64バイトの出力を作り、この出力をキーストリームとして使用する。
Poly1305もDaniel Bernsteinによって設計されており*3、Wegman-CarterのMACデータを作成する。
chacha20-poly1305@openssh.com はopenssh*4によって定義され、認証付き暗号モードとしてこれら2つ(ChaCha20とPoly 1305)のプリミティブを組み合わせている。この構造はAdam LangleyによるTLSへの提案*5のベースに使われているが、パケット長の暗号化に加えMACに渡されるデータのレイアウトに違いがある。
K_1は、メッセージサイズを明らかにすることによる情報漏洩を回避するため、暗号化されたメッセージのペイロードサイズを暗号化するために使われる必要がある。
K_2は、AEADを構築するためpoly1305と組み合わせて使用する必要がある。
ChaCha20-Poly1305の最適化実装は一般的には非常に高速なため、現在の暗号化されていないP2Pメッセージフォーマットより、暗号化されているメッセージの方が必要とするCPUサイクルは少なくなる可能性が高い。Pieter Wuilleによる現在の標準実装についての分析では、SHA256がChaCha20 & Poly1304より多くのCPUサイクルを必要とするとされている。
encackメッセージタイプ
応答側のピアはencackメッセージを送ることで暗号化要求に応える。
フィールドサイズ | 定義 | データタイプ | コメント |
---|---|---|---|
33バイト | ephemeral-pubkey | comp.-pubkey | 応答側のピアからのセッションの公開鍵 |
この時点では対称鍵暗号の共有秘密鍵は(自分の秘密鍵とリモートの公開鍵で)ECDHを使って算出する必要がある。秘密鍵が送信されることは無い。攻撃者が少なくとも1つの秘密鍵とリモートの公開鍵を知っている場合は共有秘密鍵を算出することができる。
- encinit/encackのメッセージ交換は両側から行う必要がある
- 各通信方向では対称暗号に自身の秘密鍵を使用する。
- (応答するピアからの)2つめのencinit要求では、同じ対称暗号タイプを使用する必要がある。
- (応答するピアからの)2つめのencack応答の前の暗号化されていないメッセージは全て無視する。
- encinit/encackのやりとりに成功した後は、"encrypted messages structure" を使う必要がある。また要求ピアからの暗号化されていないメッセージが送信されたらコネクションを終了する必要がある。
両側からのencinit/encackに成功した後は、メッセージフォーマットとして"encrypted messages structure"を使う必要がある。要求ピアからの暗号化されていないメッセージは接続終了につながる必要があり、それは暗号化されていないメッセージ構造の4バイトのネットワークマジックによって検出できる。
Encrypted Messages Structure
フィールドサイズ | 定義 | データタイプ | コメント |
---|---|---|---|
4 | length | uint32_t | 暗号文のペイロードのバイト数 |
? | ciphertext payload | ? | 1つもしくは複数の暗号文コマンドとメッセージデータ |
16 | MAC tag | ? | 128bitのMAC-tag |
暗号化されたメッセージには4バイトのネットワークマジックは含まれない。
最大メッセージ長は慎重に選択する必要がある。4バイトの長さのフィールドは、4GiBまでのメッセージバッファを許容する。認証が成功するまではメッセージ処理をしてはいけない。
AEADのため、4バイトのsha256チェックサムは必要ない。
両方のピアは対称暗号の初期化ベクトルを構築するためリモートピアに送信されるメッセージのメッセージ番号(int64)を追跡する必要がある。その際パディングが必要になることがある(96bit IVs)。
暗号化されたペイロードは復号され1つもしくは複数の暗号化されていないメッセージになる。
フィールドサイズ | 定義 | データタイプ | コメント |
---|---|---|---|
? | command | varlen | パケットの内容を識別するASCII文字列で、暗号化されたメッセージにvarlenを使用している。 |
4 | length payload | uint32_t | プレーンテキストペイロードの長さ |
? | payload | ? | 実際のデータ |
より多くのデータが存在する場合は、別のメッセージをデシリアライズする必要がある。
Re-Keying
応答するピアは、要求ピアに33バイトのゼロを含むencackメッセージを送ることで、encackメッセージの後の暗号化されたメッセージは全て次の対称暗号鍵で暗号化されることを通知する。
新しい対称暗号鍵は↓で計算される。
SHA256(SHA256(old_symetric_cipher_key))
鍵の変更の間隔は最小10秒というのがピアのポリシー。
(RFC4253のSSHのトランスポートプロトコルで推奨されているように)鍵の変更はデータの送受信を1GBする度に実施する必要がある。
リスク
暗号化には身元認証のスキームは含まれていない。このBIPでは暗号化の初期化中のMITM攻撃(中間者攻撃)を回避する提案は含まれてない。
アイデンティティ認証はこのBIPの後にでる別のBIPでカバーされるだろう。
互換性
この提案には下位互換性がある。非対応のピアはencinitメッセージを無視する。
まとめ&所感
- フルノード使う分には通信の暗号化というのはそこまで重要度は高くないけど、SPVノードでは話が変わってくる。SPVノードでBloom Fitler使う際の脆弱性についてはちゃんとレポート読みたいが、公開鍵と公開鍵ハッシュ両方をBloom Filterにプッシュしてるのが問題みたい。
- BIP-151ではECDHのネゴシエーションを行う際のメッセージ仕様(encinitメッセージとencackメッセージ)を定義
- ChaCha20-Poly1305については以下のブログが詳しい。
d.hatena.ne.jp - 中間者攻撃を回避するための身元確認のスキームは別のBIPで定義
- BIP上には記載されていないので参照実装はまだっぽい。
*1:SPVノードが利用しているBloom Filterに関する脆弱性のレポート参照と。
http://e-collection.library.ethz.ch/eserv/eth:48205/eth-48205-01.pdf
*2:http://cr.yp.to/chacha/chacha-20080128.pdf
*3:http://cr.yp.to/mac/poly1305-20050329.pdf
*4:https://github.com/openssh/openssh-portable/blob/05855bf2ce7d5cd0a6db18bc0b4214ed5ef7516d/PROTOCOL.chacha20poly1305
*5:http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-03