概要
この文書は、オリジンから目的ノードまでたどるために使われるオニオンルーティングされたパケットの構造について解説している。そのパケットはホップと呼ばれるたくさんの中間ノードを通過している。このルーティングスキームは Sphinx 構造をベースにしておりホップごとのペイロードで拡張されている。
メッセージをフォワードしている中間ノードは、そのパケットの真正性を検証することが可能であり、どのノードにフォワードすべきか知ることができる。しかし、サクセッサーとプレデセッサーを除いた他のどのノードがルーティングの一部であるか、もしくは自分がそのルートのどの位置にいるかを知ることはできない。パケットは各ホップで難読化されており、ネットワークレベル攻撃者が同じルートに属するパケットと関連づけられないように保証している。これは攻撃者のトラフィック分析によるパケット関連づけの可能性を排除していないことに注意されたい。
ルートは、目的ノードや各中間ノードの公開鍵を知っているオリジンノードによって組み立てられる。各ノードの公開鍵を知ることでオリジンノードは、中間ノードや目的ノードのための(ECDH を用いて)共有秘密を作ることを可能にする。共有秘密はバイトの擬似ランダムシステム(パケットを難読化する)を作ったり、多くの鍵(ペイロードを暗号化し HMACs を計算する)を生成する。HMACs は各ホップのパケット真正性を保証するために使用される。ルートに沿った各ホップは送信者ノードの ID を隠すためにオリジンノードの短期間鍵のみを参照することができる。短期間鍵は次にフォワーディングする前に、各中間ホップによって隠されてオニオンがルートに沿ってリンクされないようにする。
鍵生成
多くの暗号鍵や検証鍵は共有秘密から作られる。
- rho : ホップごとの情報を難読化するために必要な擬似ランダムバイトストリームを生成するときの鍵として使用する
- mu : HMAC 生成に使用
- um : エラー報告に使用
- pad : ミックスヘッダーパケットで始まるランダムフィルターバイトを生成するために使用
鍵は適した鍵タイプをを HMAC 鍵として使用し、共有秘密をメッセージとして HMAC を計算することで生成する。
擬似ランダムバイトストリーム
擬似ランダムバイトストリームは経路の各ホップでパケットを難読化するために使われており、各ホップは次のホップのアドレスと HMAC だけを回収することができる。擬似ランダムバイトストリームは必要な長さの 0x00 バイトストリームを(ChaCha20
を使用して)暗号化することで生成される。
鍵は再利用されないので、固定ノンスの使用は安全である。
パケット構造
パケットは4つのセクションでできている:
- version byte
- 圧縮 secp256k1 公開鍵(33 byte): 共有秘密の生成中に使用される
hop_payloads
(1,300 byte): 複数の可変長のhop_payloadペイロードまたは最大20個の固定サイズのレガシーhop_dataペイロードで構成される1300バイトのhop_payloads- hmac(32 byte): パケット真正性検証に使われる
パケットのネットワークフォーマットは1つの隣接したバイトストリームにシリアライズ化された個別セクションで構成されており、パケット受信者に転送される。
パケットの全体構造は以下のようになる:
- type :
onion_packet
- data :
[byte : version]
[point : public_key]
[1300*byte : hop_payloads]
[32*byte : hmac]
hop_payloads
フィールドは難読化されたルーティング情報と関連する HMAC を含んだ構造になっている。1,300 byte になっており、構造は以下のようになっている:
- type :
hop_payloads
- data :
[bigsize : length]
[hop_payload_length : hop_payload]
[32*byte : hmacs]
- ...
filler
hop_payload
フィールドを用いることで、オリジンノードは各ホップでフォワードされる HTLCs の構造と経路を特定することができる。hop_payloads
はパケットワイド HMAC 下で保護されているため、それに入っている情報は経路上の HTLC 送信者と各ホップ間のペア関係で完全に認証できる。このエンドツーエンド認証を用いて、各ホップは hop_payload
で特定された値で HTLC パラメタをクロスチェックして、送信側のピアが不正に作成されたHTLCを転送していないことを確認できる。
支払いの受理とフォワーディング
ノードは一度ペイロードをデコードすると、ローカルにその支払いを受理するか、そのペイロードで指定された次のホップを示すノードにフォワードするかどちらかを選択する。
非厳格フォワーディング
あるノードは、受信ノードが short_channel_id
で指定された同じノードの公開鍵を持っている限り、short_channel_id
で指定された一つ以外の出力チャネルに沿って HTLC をフォワードする可能性がある。従って、もし short_channel_id
がノード A と B を接続する場合、その HTLC は A と B に接続する任意のチャネルを横切ってフォワードすることができる。順守しないと、受信者はオニオンパケットのネクストホップを復号できなくなる。
基本原則
2つのピアが複数のチャネルを持つ場合、下流ノードはそのパケットがどのチャネルで送られるかに関わらず次のホップペイロードを複合することができるだろう。
厳格でないフォワーディングを実装しているノードは個別ピアでチャネルバンド幅のリアルタイム評価をすることが可能になり、ローカルに最適なチャネルを使用可能になる。例えば、short_channel_id
で指定された A と B が接続するチャネルがフォワーディングタイムで十分なバンド幅を持っていない場合、A はフォワードする他のチャネルを使うことが可能である。このように short_channel_id
でのバンド幅制約により HTLC が失敗することを防ぐことによって支払いの遅延を減らすことができる。
厳格でないフォワーディングは、そのチャネルがパブリックなチャネルグラフで知られていなくても受信ノードへのプライベートなチャネル接続の使用を可能にする。
推奨
厳格でないフォワーディングを用いた実装では、送信者は最も安いコストのチャネルを結果として選ぶので、同じピアで全てのチャネルに同じ料金スケジュールを適用することを考慮すべきである。個別のポリシーがあると、転送ノードは、同じピアを持つすべてのチャネルにわたって集約帯域幅を提供している場合でも、送信者に最適な料金スケジュールに基づいて料金を受け入れる可能性がある。
また、実装は代わりのチャネルを使用することで彼らの予想した料金収入を逸脱しないことを確実にするために同様のポリシーチャネルにのみ非厳密なフォワーディングを適用することを選択できる。
最終ノードへの支払い
ルートを構築するとき、オリジンノードは以下の値を用いて最終ノードへのペイロードを用いる:
payment_secret
: 受取人によって指定されたペイメントシークレットを設定するoutgoing_cltv_value
: 受取人によって指定された最終有効期限を設定するamt_to_forward
: 受取人によって指定された最終的な量を設定する
これは最終ノードがこれらの値を確認し、必要であればエラーを返すことが可能である。しかし、最後から2番目のノードによるプロービング攻撃の可能性も排除する。そうでなければ、そのような攻撃は、異なる量/有効期限で HTLC を再送信することによって、受信ピアが最後のものであるかどうかを発見しようとする可能性がある。最終ノードは受け取ったその HTLC からオニオンペイロードを抽出し、その値を HTCL の値と比較する。
共有秘密
起点ノードは、楕円曲線 Diffie-Hellman(ECDH)を使用して、ルートに沿った各ホップと、そのホップの送信者の一時鍵とホップのノードID鍵の間で共有シークレットを確立する。結果の曲線ポイントは、圧縮形式にシリアル化され、SHA256 を使用してハッシュされる。 ハッシュ出力は、32 バイトの共有秘密として使用される。
ECDH は曲線ポイントを出力する EC 公開鍵と EC 秘密鍵に対する操作である。パケット構築の間、ECDH の入力として送信者は一時秘密鍵とホップの公開鍵を使用するが、パケットフォワード中はそのホップは一時鍵と自身のノードID秘密鍵を使用する。ECDH の性質によって、それらは同じ値を生成する。
パケット構築
n_0
: 送信ノード(オリジンノード)
受信ノード(最終ノード)にパケットを送信したいn_r
: 受信ノード
はじめに、送信者は経路 {n_0
, n_1
, ..., n_r
} を計算する。送信者は経路上ノードの公開鍵を集め、32-byte のランダムな sessionkey
を生成する。送信者は関連するデータを渡す可能性がある(それはパケット自身には含まれない)。関連データは HMAC に含まれ、各ホップでの真正性検証の間、提供された関連データに一致しなければならない。
オニオンを構築するために、送信者は最初のホップの一時的な秘密鍵 ek_1
を sessionkey
に初期化し、secp256k1 ベースポイントを乗算することにより、対応する一時的な公開鍵 epk_1
を取得する。経路上の k ホップの場合、送信者は共有秘密 ss_k
とネクストホップの一時的鍵 ek_{k + 1}
を反復的に以下のように計算する:
- 曲線座標を得るために送信者はホップの公開鍵と一時的秘密鍵で ECDH を実行する(共有秘密
ss_k
を生成するために SHA256 でハッシュされる) - ブラインド因子は一時的公開鍵
epk_k
と共有秘密ss_k
の連結の SHA256 ハッシュである - ネクストホップの一時的秘密鍵
ek_{k+1}
は、ブラインド因子による現在の一時的秘密鍵ek_{k}
の乗算で計算される - ネクストホップの一時的公開鍵
epk_{k+1}
は、一時的秘密鍵ek_{k+1}
をベースポイントで乗算することで得られる。
送信者が上記の情報を全て入手すると、パケットを構築する。r
ホップのパケットを作成するには r
の 32-byte 一時的公開鍵、r
の 32-byte 共有秘密、r
の 32-byte ブラインド因子、r
の可変長ペイロード hop_payload
を必要とする。構築すると、受信ピアのアドレスと 1336-byte パケットが得られる。パケット構築は経路の反対から実行される(最終ホップから行う)
中身は共有秘密を用いて作られる。経路の各ホップを逆順に、送信者は以下の操作を適用する:
- rho 鍵 と mu 鍵はホップの共有秘密を用いて生成される
shift_size
はhop_payload
とその長さとその HMAC の長さのビッグサイズエンコーディングの長さと定義されている。もしペイロードの長さがI
でshift_size
が1 + I + 32
(I < 253
の場合)であり、そうでない場合はI
のビッグサイズエンコーディングにより3 + l + 32
であるhop_payload
フィールドはshitf_size
によって右よりにシフトされ、1300バイトのサイズを超える最後のshift_size
バイトを破棄する- ビッグサイズにシリアライズされた長さ、シリアライズされた
hop_payload
、および hmac は、次のshift_size
バイトにコピーされる - rho 鍵は擬似ランダムバイトストリームの 1,300-byte を生成するために使用される
- 最終ホップの場合(つまり最初の反復)、
hop_payloads
フィールドのテールはルーティング情報中身で上書きされている - 次の HMAC は
hop_payloads
と関連データを連結し、HMAC 鍵として mu 鍵を使用して計算される。
最終的な HMAC の値は経路上の最初の受信ピアによって使用される HMAC である。
パケット生成は version
バイトと最初のホップの一時的公開鍵、最初のホップの HMAC、難読化された hop_payload
を含んだパケットを返す。
パケットフォワーディング
この仕様は version
0 のパケットに限定される。
パケットを受け取ると、パケットが自分のサポートする version
かどうかを比較する。サポートしているパケットの場合、ノードはまずパケットを解析する。次に自身の公開鍵に対応する秘密鍵とパケットから一時鍵を用いて共有秘密を計算する。
上記の要件は、トラフィック分析を介して支払いの進行状況を追跡しようとして、ルートに沿ったホップが支払いを複数回再試行することを防ぐ。このようなプロービングを無効にするには、以前の共有シークレットまたはHMACのログを使用して実行できる。これは、HTLC が受け入れられなくなった後に忘れられる可能性がある。そのようなログは確率論的なデータ構造を用いる可能性があるが、このログの最悪の場合のストレージ要件または誤検知を制限するために、必要に応じてコミットメントをレート制限する必要がある。
次に、ノードは mu 鍵を計算するため(結果として hop_payloads
の HMAC を計算する)に共有秘密を使用する。その HMAC はパケットの HMAC にと比較する。この比較はじょう情報漏洩を避けるために時間的にコンスタントである必要がある。
この時点で、そのノードは rho 鍵と gamma 鍵を生成することができる。ルーティング情報は難読化がとかれ、ネクストホップの情報が取り除かれる。そのために、ノードは hop_payloads
フィールドをコピーし、1300-byte の 0x00
を追加し、rho 鍵を用いて 2*1300-byte の擬似ランダムを生成し、XOR を用いて結果をコピーに適応する。
最初の数バイトは、ビッグサイズでエンコードされた hop_payload
の長さ I
に対応し、その後の1バイトのルーティング情報が hop_payload
になり、32バイトの HMAC
になる。次の 1300-byte は出力パケットの hop_payloads
の 1300-byte である。
32 の 0x00
は特別な hmac 値は現在処理中のホップは意図された受信者であり、パケットは転送されるべきではないことを示している。HMAC
がルートの終了を示さず、ネクストホップが処理ノードのピアである場合、 新しいパケットがアセンブルされる。パケットアセンブリは、共有シークレットとともに処理ノードの公開鍵で一時鍵をブラインドし、hop_payloads
をシリアル化することによって実現される。結果のパケットは、アドレス指定されたピアに転送される。
中身の生成
パケットを受け取ると、処理ノードはルート情報とホップごとの payload
からパケット宛の情報を抽出する。抽出は難読化をとき、フィールドを左シフトすることで行われる。これは各ホップでそのフィールドを短くしており、攻撃者はそのルート長を推測することが可能になる。このため、このフィールドはフォワーディングする前に追加される。そのパディングは HMAC の一部であるため、オリジンノードは各ホップの HMAC を正しく計算するために、(各ホップが生成するものと)同一のパディングを事前に生成する必要があります。その中身は選択されたルートでは 1300-byte よりも短い場合、フィールド長を埋めるためにも使われる。
hop_payloads
の難読化をとく前に、処理ノードは全長が 2*1300-byte になるようにそれを 1300-byte の 0x00
でパディングする。次に、長さが一致する疑似ランダムバイトストリームを生成し、XOR を使用して hop_payloads
に適用する。 これにより、宛先の情報が難読化されなくなり、同時に最後に追加された 0x00
バイトが難読化される。
正確な HMCA を計算するために、オリジンノードは各ホップごとに 、各ホップによって増加的に追加される難読化されるパディングを含んでいる hop_payloads
を事前に生成する必要がある。この増加的に難読化されたパディングはその中身と呼ばれる。
エラー
オニオンルーティングプロトコルは暗号化されたエラーメッセージをオリジンノードに返すためのシンプルなメカニズムを含んでいる。返されたエラーメッセージは最終ノードを含む任意のホップによって報告された失敗である可能性がある。オリジン以外のホップはその生成に必要な情報にアクセスできないため、フォワードパケットのフォーマットはリターンパスには使用できない。これらのエラーメッセージは、ホップ障害の可能性があるためチェーン上に配置されないため、信頼できないことに注意されたい。
中間ホップはフォワードパスから共有秘密を保存し、各ホップ中の一致するリターンパケットを難読化するために再利用する。加えて、各ホップはローカルに自身の送信ピアに関するデータを保存するため、最終的なリターンパケットをどこにリターンフォワードするかを認識している。
エラーメッセージを生成したノード(エラーノード)は以下のフィールドを持つリターンパケットを作る:
- data :
[32*byte : hmac]
[u16 : failure_len]
[failure_len*byte : failuremsg]
[u16 : pad_len]
[pad_len*byte : pad]
hmac
はそのパケットの残りの部分を認証する HMAC であり、上記のプロセスを使用して生成されたキー、キータイプ um、以下に定義する failuremsg
、および長さを隠すために使用される追加バイトとしての pad
を使用する。
エラーノードは ammag
タイプの鍵を用いて、新たな鍵を生成する。この鍵は XOR を用いてパケットに適応する擬似ランダムストリームを生成するために使われる。難読化ステップはリターンパスに沿った全てのホップによって繰り返される。リターンパケットを受け取った時、各ホップはその ammag
、擬似ランダムバイトストリームを生成し、リターンフォワードする前に結果をそのパケットに適応する。
オリジンノードは、それがリターンメッセージの意図された最終受信者であることを検出できる。これは、対応する転送パケットの発信元であったためである。オリジンノードは、開始した転送に一致するエラーメッセージを受信すると(つまり、エラーをそれ以上リターンフォワードできない)、ルート内の各ホップの ammag
鍵と um
鍵を生成する。その後、各ホップの ammag
鍵を用いてエラーメッセージを反復的に復号し、各ホップの um 鍵を使って HMAC を計算する。オリジンノードは計算した HMAC と hmac
フィールドと一致することによってエラーメッセージの送信者を検知することが可能である。
フォワードパケットとリターンパケット間の関連付けは、支払いチャネルの HTLC との関連付けを介してこのオニオンルーティングプロトコルの外部で処理される。