Lightning NetworkにおけるNoise(Noise_XK)
Lightning Networkの仕様書としてはBOLTがある。noise protocolはLightning Messageのやりとりをするのに必要な「事前のやりとり」の定義であり、それはbolt8に記される。
しかし、全ての詳細が記されているわけではなく、かつ文章だけでは理解が難しいため、必要に応じ、LNDの挙動を観察している。
概要
Lightning Node間での全てのやりとりは秘密で実施されるべきで、そのために事前に暗号化される。bitcoin同様のsecp256k1で作成されたpublic keyは長期的なlightning nodeのアイデンティティとして作用し、この暗号化にも利用される。
下記は公開鍵作成のコード例。
const ecdsa = new elliptic.ec('secp256k1');
const ECpair = ecdsa.genKeyPair();
Noise Protocol
SSLに代表される通信暗号化は、悪意ある第三者からの攻撃を防ぐ為に必須となっているが、一般的にはクライアントサーバー間での通信に利用される。
Noiseは、エンドツーエンド暗号化に特化した新しいプロトコルで、軽量であるため採用されている。
state machine
Noiseは、双方により管理される変数と、交換されるメッセージによって作用する。参加者は下記の値を管理する。
- s, e:
sはstaticはkey pair、eはephemeral、一時的なkey pair。 - rs, re:
rはremotesを指す。 - h:
すべての送受信されたデータはhashとして保持される。 - ck:
前回のDHの結果をhash化して保持する。handshakeが完了したあとはchaining keyはメッセージの復元に利用される。 - k, n:
暗号化用の鍵をk、そのnonceをnとする。新しいDHが実施されckが更新される毎にkも再計算される。kとnはsとpayloadの暗号化に利用される。kによる暗号化を実施する際はhも利用され、handshake phaseでの秘匿性を提供する。
handshake messageはpayloadに続くDH公開鍵によって構成される。payloadは証明書やその他のデータを含む。handshake messageを送るためには、送信者はpayloadを指定してメッセージパターンに基づいて順番にプロセスをこなしていく必要がある。
"e":
送信者はeを作成して保持し、平文で送信する。この公開鍵は古いhでhashされてそれを新しいhとする。"s":
送信者はsをkを使って暗号化して送信する。この公開鍵は古いhでhashされてそれを新しいhとする。"ee", "se", "es", "ss":
DHが作成者によって実施される。sを使うかeを使うかは一文字目で決定する。この共有鍵は古いckでhashされ、それを新しいckとする。nは0に初期化される。
すべてのmessage交換が完了したら、kによって暗号化したメッセージを送信し、hを更新する。
簡単な例
未認証でのhandshakeは、下記の形で示される。
-> e
<- e, ee
- 送信者が、eの公開鍵を送る。ちなみに、このeは、一時的なECpairである。
- 受信者が、eの公開鍵を送る。eeは、DHが実行された共有鍵である。
const shared = remote.derive(e.getPublic());
最初に送られるeとpayload、返信されるeは平文で、その後は暗号化される。 若干違うパターンとして、受信者が暗号化されたsを認証付きで送る方法がある。
-> e
<- e, ee, s, es
esは、送信者のeと受信者のsの共有鍵であり、送信者も復元することが可能。ck、kはee、esのhashの結果となる。
送信者はsとseを送ることで、同様に自身を証明できる。これがnoise protocolの原型となるやりとりである。
-> e
<- e, ee, s, es
-> s, se
Noise_XK
Handshakeには、Noise_XKが利用される。
これはnoise protocolのうち、interactive-handshake-patterns-fundamentalに当てはまる。
大きく下記3つのオブジェクトが必要となる。
- hash関数としてsha256
- 楕円曲線暗号としてsecp256k1
- AEADとしてChaChaPoly-1305
Handshake State
下記の関数を両者が管理する必要がある。
ECDH(k, rk)
プライベートキーkと、remoteのpublic key rkでECDHを実行する。計算結果はsha256で圧縮される。HKDF(salt,ikm)
共有鍵とHMAC、salt(乱数)から鍵を生成する。encryptWithAD(k, n, ad, plaintext)
送信データはencryptWithADにより暗号化される。ChaCha20-Poly1305というTLSの暗号方式を採用している。decryptWithAD(k, n, ad, ciphertext)
上記3で暗号化されたテキストを復元する。
Initialization
実際のhand shakeを開始する前に、下記のhand shakeを実施する必要がある。
h = SHA-256(protocolName)
protocol nameは、NoiseXKsecp256k1ChaChaPolySHA256である。ck = h
h = SHA-256(h || prologue)
a || bはaとbの結合を表す。prologueはlightningである。