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は、双方により管理される変数と、交換されるメッセージによって作用する。参加者は下記の値を管理する。

  1. s, e:
    sはstaticはkey pair、eはephemeral、一時的なkey pair。
  2. rs, re:
    rはremotesを指す。
  3. h:
    すべての送受信されたデータはhashとして保持される。
  4. ck:
    前回のDHの結果をhash化して保持する。handshakeが完了したあとはchaining keyはメッセージの復元に利用される。
  5. k, n:
    暗号化用の鍵をk、そのnonceをnとする。新しいDHが実施されckが更新される毎にkも再計算される。kとnはsとpayloadの暗号化に利用される。kによる暗号化を実施する際はhも利用され、handshake phaseでの秘匿性を提供する。

handshake messageはpayloadに続くDH公開鍵によって構成される。payloadは証明書やその他のデータを含む。handshake messageを送るためには、送信者はpayloadを指定してメッセージパターンに基づいて順番にプロセスをこなしていく必要がある。

  1. "e":
    送信者はeを作成して保持し、平文で送信する。この公開鍵は古いhでhashされてそれを新しいhとする。

  2. "s":
    送信者はsをkを使って暗号化して送信する。この公開鍵は古いhでhashされてそれを新しいhとする。

  3. "ee", "se", "es", "ss":
    DHが作成者によって実施される。sを使うかeを使うかは一文字目で決定する。この共有鍵は古いckでhashされ、それを新しいckとする。nは0に初期化される。

すべてのmessage交換が完了したら、kによって暗号化したメッセージを送信し、hを更新する。

簡単な例

未認証でのhandshakeは、下記の形で示される。

-> e
<- e, ee

  1. 送信者が、eの公開鍵を送る。ちなみに、このeは、一時的なECpairである。
  2. 受信者が、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つのオブジェクトが必要となる。

  1. hash関数としてsha256
  2. 楕円曲線暗号としてsecp256k1
  3. AEADとしてChaChaPoly-1305

Handshake State

下記の関数を両者が管理する必要がある。

  1. ECDH(k, rk)
    プライベートキーkと、remoteのpublic key rkでECDHを実行する。計算結果はsha256で圧縮される。

  2. HKDF(salt,ikm)
    共有鍵とHMAC、salt(乱数)から鍵を生成する。

  3. encryptWithAD(k, n, ad, plaintext)
    送信データはencryptWithADにより暗号化される。ChaCha20-Poly1305というTLSの暗号方式を採用している。

  4. decryptWithAD(k, n, ad, ciphertext)
    上記3で暗号化されたテキストを復元する。

Initialization

実際のhand shakeを開始する前に、下記のhand shakeを実施する必要がある。

  1. h = SHA-256(protocolName)
    protocol nameは、NoiseXKsecp256k1ChaChaPolySHA256である。

  2. ck = h

  3. h = SHA-256(h || prologue)
    a || bはaとbの結合を表す。prologueはlightningである。