QUIC: A UDP-Based Multiplexed and Secure Transport を読んで要点だけをまとめた自分用の資料です。 読んでいる時点では draft-24 です。
QUIC: A UDP-Based Multiplexed and Secure Transport
QUIC: UDPに基づく多重化・セキュアなトランスポート層のプロトコル
概要
- この文書ではQUICトランスポートプロトコルの主要な部分について定義する
- 付随するドキュメントはQUICの損失検知、輻輳制御、TLSによる鍵交換について説明する
1. はじめに
- QUICは多重化されたセキュアな汎用トランスポートプロトコル
- 以下の機能を提供する
- ストリームの多重化
- ストリームと接続レベルのフロー制御
- 低遅延な接続確立
- 接続の移行とNATへの柔軟性
- 認証付きで暗号化されたヘッダとペイロード
- QUICでは、対応していないクライアントやミドルボックスによる変更要求を回避するためにUDPを基盤として用いる
- QUICは全てのヘッダを認証し、ほとんどのデータを暗号化する
- エラーを知らせるデータ(シグナル)も暗号化される
1.1. 内容
- ストリーム(Streams)は基本的なサービスを表す抽象的なもの
- 第2章 : ストリームに関する概念の説明
- 第3章 : ストリームの状態モデル
- 第4章 : フロー制御の概要
- 接続(Connections)はQUICエンドポイントが通信する内容
- 第5章 : 接続に関する概念の説明
- 第6章 : バージョン交渉
- 第7章 : 接続確立のプロセス
- 第8章 : DoS攻撃への対策
- 第9章 : 接続を新しいネットワークパスへ移行する方法
- 第10章 : 接続終了のオプション
- 第11章 : エラーハンドリング
- パケットとフレームはQUICで通信するための基本的な単位
- 第12章 : パケットとフレームに関する概念の説明
- 第13章 : 送信、再送信、確認応答のモデル
- 第14章 : パケットサイズの管理ルール
- QUIC要素のエンコーディング
- 第15章 : バージョン(Versions)
- 第16章 : 整数
- 第17章 : パケットヘッダ
- 第18章 : トランスポートのパラメータ
- 第19章 : フレーム
- 第20章 : エラー
- 付随するドキュメント
- QUIC-RECOVERY : QUICの損失検出と輻輳制御について
- QUIC-TLS : TLSによる鍵交換について
- QUIC-INVARIANTS : QUICの一般的な特性について
1.2. 用語と定義
- QUIC : トランスポート層のプロトコルの名前
- QUICパケット : QUICの処理可能なデータグラムの単位。複数のQUICパケットは1つのUDPデータグラムに格納できる。
- 確認応答誘発(Ack-eliciting)パケット : ACK, PADDING, CONNECTION_CLOSE以外のフレームを含むQUICパケット。確認応答誘発パケットを受信したエンドポイントはACKを返信する。
- エンドポイント : QUIC接続に参加し、パケットの生成、受信、処理を行う実体。2者間ではクライアントとサーバだけがエンドポイントになる
- クライアント : QUIC接続を開始するエンドポイント
- サーバ : QUIC接続開始を受信するエンドポイント
- 接続ID : QUIC接続を識別するための文字列
- ストリーム : QUIC接続内の双方向または単方向のチャネル。QUIC接続では同時に複数のストリームを扱うことができる
1.3. 表記規則
-
[x]
: xは任意 -
x (A)
: xはAビットの固定長 -
x (A/B/C)
: xは(A or B or C)ビットの固定長 -
x (i)
: xは可変長の整数エンコード -
x (*)
: xは可変長
2. ストリーム
- ストリーム管理に関する他のプロセス (終了、中止、フロー制御) は全て最小限のオーバーヘッドとなるように設計されている
- ストリームは、接続の全期間に渡って維持されることもある
- ストリームはいずれかのエンドポイントによって作成される
- ストリームでは他のストリームと同時にデータ送信や送信中止ができる
- QUICは、異なるストリーム上では、バイト間の順序は保証しない
- QUICは任意の数のストリームを同時に操作し、任意の量のデータを、任意のストリームで送信できる
2.1. ストリームの種類と識別子
- ストリームは単方向・双方向どちらでもよい
- 接続内のストリームは、ストリームID (stream ID) によって識別される
- ストリームIDは62ビット整数 (0〜2^62-1) で表され、全てのストリームに対して一意(ユニーク)となる
- ストリームIDは可変長整数としてエンコードされる (第16章参照)
- エンドポイントは接続内でストリームIDを再利用してはいけない
- ストリームIDの最下位ビットでストリームの開始者を識別する
- クライアントが開始したストリームは偶数のストリームID (最下位ビットが0)
- サーバが開始したストリームは奇数のストリームID (最下位ビットが1)
- ストリームIDの2番目の最下位ビットは双方向・単方向を区別する
表1: Stream ID Types
ビット | ストリームの種類 |
---|---|
0x0 | クライアント側から開始した双方向のストリーム |
0x1 | サーバ側から開始した双方向のストリーム |
0x2 | クライアント側から開始した単方向のストリーム |
0x3 | サーバ側から開始した単方向のストリーム |
2.2. データの送信と受信
- STREAMフレームは、アプリケーションによって送信されたデータをカプセル化する
- エンドポイントはストリームIDとオフセットフィールドをSTREAMフレームに順番に配置する
- エンドポイントは順序付けされたバイト列を送信できる
- エンドポイントは相手から通知されたフロー制御の限度まで、データをバッファリングする必要がある
- 実装ではデータを順番通りに配信しない機能を提供することができる (MAY)
- エンドポイントは同じストリームオフセットで複数回ストリームのデータを受信できる
- オフセットは変更してはいけない
- オフセットが異なる場合は PROTOCOL_VIOLATION エラーとして扱う
- エンドポイントは相手が設定したフロー制御の制限内にあることを確認せずにデータを送信してはいけない
2.3. ストリームの優先順位
- ストリームの多重化はパフォーマンスにおいて大きな影響を与える
- QUICでは優先順位の情報を交換するメカニズムを提供しないが、代わりにQUICを使用するアプリケーションからの優先順位を用いる
- QUICの実装では、アプリケーションがストリームの優先順位を示す方法を提供する (SHOULD)
2.4. ストリーム上の必要な操作
- ストリームの送信部分
- データの書き込み、フロー制御の理解
- ストリームの終了、結果としてFINビットを設定したSTREAMフレームの生成
- ストリームのリセット
- ストリームの受信部分
- データの読み込み
- 読み取りの中止とストリームの終了、結果としてSTOP_SENDINGフレームの生成
- フロー制御によってストリームの状態が変更したら、それを相手に通知する
3. ストリームの状態
3.1. 送信側のステートマシン
o
| Create Stream (Sending)
| Peer Creates Bidirectional Stream
v
+-------+
| Ready | Send RESET_STREAM
| |-----------------------.
+-------+ |
| |
| Send STREAM / |
| STREAM_DATA_BLOCKED |
| |
| Peer Creates |
| Bidirectional Stream |
v |
+-------+ |
| Send | Send RESET_STREAM |
| |---------------------->|
+-------+ |
| |
| Send STREAM + FIN |
v v
+-------+ +-------+
| Data | Send RESET_STREAM | Reset |
| Sent |------------------>| Sent |
+-------+ +-------+
| |
| Recv All ACKs | Recv ACK
v v
+-------+ +-------+
| Data | | Reset |
| Recvd | | Recvd |
+-------+ +-------+
図1: States for Sending Parts of Streams
- STREAMまたはSTREAM_DATA_BLOCKEDフレームを送信すると「Send」状態に移る
- 双方向ストリームにおいて、受信側が「Recv」状態なら、送信側が「Ready」状態に入ってすぐに「Send」状態に移る
- 「Send」状態では、STREAMフレームを送信し、必要に応じて再送信する
- FINビットを含むSTREAMフレームを送信すると「Data Sent」状態に移る
- 送信側が受信側から全ての確認応答を受信したとき、「Data Recvd」状態に移り、終了する
- 「Ready」「Send」「Data Sent」状態のときに、相手から STOP_SENDING フレームを受信したら、RESET_STREAM フレームを送信して、「Reset Sent」状態に移る
3.2. 受信側のステートマシン
o
| Recv STREAM / STREAM_DATA_BLOCKED / RESET_STREAM
| Create Bidirectional Stream (Sending)
| Recv MAX_STREAM_DATA / STOP_SENDING (Bidirectional)
| Create Higher-Numbered Stream
v
+-------+
| Recv | Recv RESET_STREAM
| |-----------------------.
+-------+ |
| |
| Recv STREAM + FIN |
v |
+-------+ |
| Size | Recv RESET_STREAM |
| Known |---------------------->|
+-------+ |
| |
| Recv All Data |
v v
+-------+ Recv RESET_STREAM +-------+
| Data |--- (optional) --->| Reset |
| Recvd | Recv All Data | Recvd |
+-------+<-- (optional) ----+-------+
| |
| App Read All Data | App Read RST
v v
+-------+ +-------+
| Data | | Reset |
| Read | | Read |
+-------+ +-------+
図2: States for Receiving Parts of Streams
- STREAM や STREAM_DATA_BLOCKED, RESET_STREAM を受信したとき、ストリームを作成する
- MAX_STREAM_DATA や STOP_SENDING を受信したとき、既存のストリームIDよりも大きいIDでストリームを作成する
- 「Recv」状態では、受信したデータを正しい順番で再び組み立てる
- MAX_STREAM_DATA を送信することで、相手により多くのデータを送信できるようにさせる
- FINビットを含むSTREAMフレームを受信すると、送信されたデータの合計サイズがわかる
3.3. 許可されたフレームタイプ
- 送信側
- STREAM
- STREAM_DATA_BLOCKED
- RESET_STREAM
- 送信側は終了状態で上記のフレームを送ってはいけない
- RESET_STREAMを送信後に、STREAMやSTREAM_DATA_BLOCKEDを送ってはいけない
- 受信側
- MAX_STREAM_DATA
- STOP_SENDING
3.4.双方向のストリーム状態
- 双方向ストリームは送信部と受信部から成る
- 以下の表はストリームの状態とHTTP/2の状態の対応関係を表した
表2: Possible Mapping of Stream States to HTTP/2
送信部 | 受信部 | 状態 |
---|---|---|
No Stream/Ready | No Stream/Recv *1 | idle |
Ready/Send/Data Sent | Recv/Size Known | open |
Ready/Send/Data Sent | Data Recvd/Data Read | half-closed (remote) |
Ready/Send/Data Sent | Reset Recvd/Reset Read | half-closed (remote) |
Data Recvd | Recv/Size Known | half-closed (local) |
Reset Sent/Reset Recvd | Recv/Size Known | half-closed (local) |
Reset Sent/Reset Recvd | Data Recvd/Data Read | closed |
Reset Sent/Reset Recvd | Reset Recvd/Reset Read | closed |
Data Recvd | Data Recvd/Data Read | closed |
Data Recvd | Reset Recvd/Reset Read | closed |
3.5. 要請された状態遷移
- アプリケーションがデータの受信を止めたい場合は、エラーコードを送信して中断することができる
- 「Recv」か「Size Known」状態のときは、STOP_SENDINGフレームを送信することで中断する
- STOP_SENDINGフレームを受信したエンドポイントは、必ずRESET_STREAMフレームを送信する
- RESET_STREAMフレームを送信するときは、STOP_SENDINGのエラーコードを含める必要がある
- 双方向ストリームで両方向の通信とも終了したいエンドポイントは、RESET_STREAMを送信することで一方向(自分から相手)を終了でき、STOP_SENDINGを送信することで反対方向(相手から自分)の迅速な終了を促すことができる
4. フロー制御
- 受信者はバッファリングするデータ量を制限することで、高速送信者が低速受信者を困らせたり、悪意のある送信者によって大量のメモリを消費させられたりするのを防ぐ
- 接続内の並列処理を制限するために、エンドポイントは相手が開始できるストリームの最大累積数を制御する
- CRYPTOフレームで送信されるデータは、ストリームデータと同じ方法でフロー制御されない
- 実装では、QUICのバッファリング制限を知らせるためのインターフェースを提供する (SHOULD)
4.1. データのフロー制御
- QUICではクレジットに基づくフロー制御 (credit-based flow-control scheme) を用いる
- このフロー制御は HTTP/2 に似ていて、受信者は指定したストリーム上と全接続で受信できるバイト数を通知する
- これにより、QUICで2つのレベルのデータフロー制御ができる
- ストリームフロー制御 : 1つのストリームで送信できるデータ量を制限する
- 接続フロー制御 : 全てのストリームで送信されるデータ量の合計を制限する
- 受信者はハンドシェイク中にクレジットを設定する
- 受信者は MAX_STREAM_DATA や MAX_DATA フレームを送信することで追加のクレジットを与える
- MAX_STREAM_DATA はストリームの最大バイトオフセットを示す
- 送信者がフロー制御クレジットを使い果たす前に、MAX_STREAM_DATA を送信する (MAY)
- MAX_DATA はストリーム全体の合計バイトオフセットを示す
- 送信者が制限を違反したとき、受信者は FLOW_CONTROL_ERROR エラーで接続を終了しなければならない (MUST)
- 送信者はフロー制御の制限を増やさない MAX_STREAM_DATA や MAX_DATA を無視する (MUST)
- 送信者がフロー制御の制限に達したとき、STREAM_DATA_BLOCKED か DATA_BLOCKED を送信して、データはまだあるがフロー制御によってブロックされていることを通知する (SHOULD)
- 送信者はデータ制限が同じ状態のときに複数の STREAM_DATA_BLOCKED や DATA_BLOCKED を送信してはいけない (SHOULD NOT)
4.2. フロークレジットの増加
- フロー制御の制限が大きくなると、リソースの消費量は増える
- 一般的なTCPと同様に、往復時間の推定値とアプリケーションのデータ量に基づいて、フロークレジットの増加量と通知頻度を調整するために、自動チューニングメカニズムを使うことができる
- 送信者がブロックされることを予測して、受信者は MAX_DATA や MAX_STREAM_DATA を送信し、より多くのクレジットを与える
- 通信速度が低下するので、受信者は STREAM_DATA_BLOCKED や DATA_BLOCKED を受信するのを待ってから MAX_STREAM_DATA や MAX_DATA を送信してはいけない (MUST NOT)
4.3. ストリームの中断処理
- エンドポイントは、フロー制御の制限超過やデッドロックを回避するため、フロー制御のクレジット量に同意する必要がある
- RESET_STREAM を受信すると、それ以降のデータを無視する
- RESET_STREAM にはストリームに流したデータの量を含めることで、受信者はあとどれくらいのデータが到達すれば終了なのかがわかる
- RESET_STREAM は一方向(自分から相手)のストリームを終了する
- 双方向のとき、RESET_STREAM は反対方向(相手から自分)のストリームのデータ制御に影響しない
- 双方向のとき、ストリームは「終了」状態に遷移するか、CONNECTION_CLOSE を送信するまでは、ストリームを維持しなければならない (MUST)
4.4. ストリームの最終サイズ
- 最終サイズは、ストリームによって消費されたクレジット量。つまり送信されたバイト数
- リセットされたストリームのときは、RESET_STREAMに最終サイズが含まれる
- 最終サイズは「オフセット」+「FINのSTREAMフレームの長さ」
- エンドポイントは受信部の「Size Known」か「Reset Recvd」状態に遷移したときに最終サイズを知る
- エンドポイントは最終サイズ以上のデータを送信してはいけない (MUST NOT)
- 最終サイズを知った後に、最終サイズを変更することはできない。もし、最終サイズの変更を表す STREAM や RESET_STREAM を受信したら、FINAL_SIZE_ERROR エラーで応答する (SHOULD)
- ストリームが閉じられた後でも、最終サイズ以上のデータを受信したら、FINAL_SIZE_ERROR エラーとして扱う (SHOULD)
- これらのエラーは必須ではないが、ストリームは閉じても、その閉じたストリームの最終サイズを保持する必要があることを意味する
4.5. 並列性の制御
- エンドポイントは、相手が開くことのできるストリーム数を制限する
- (ストリームの最大数 * 4 + タイプの初期ストリームID) 未満のストリームIDを持つストリームのみを開くことができる
- 初期の制限はトランスポートパラメータで設定され、MAX_STREAMS で通知される
- 単方向と双方向では別の制限が適用される
- max_streams トランスポートパラメータや受信した MAX_STREAMS が 2^60 より大きい場合は、可変長整数で表現できない最大ストリームIDが許可されるので、どちらかを受信した場合は STREAM_LIMIT_ERROR エラーですぐに閉じなければならない (MUST)
- 一度 MAX_STREAMS でストリームの制限を通知した後に、その制限よりも小さい制限を通知しても無視される
- 受信者はストリームの制限を増やさない MAX_STREAMS フレームを無視する (MUST)
- 相手の制限のために新しいストリームを開けない時は、STREAMS_BLOCKED を送信する (SHOULD)
- STREAMS_BLOCKED シグナルはデバッグに有効
- 通信速度が低下するため、エンドポイントは STREAMS_BLOCKED を受信するのを待ってから、追加のクレジットを通知してはいけない (MUST NOT)
5. 接続
- QUICの接続確立は、バージョン交渉と暗号化およびトランスポートハンドシェイクを組み合わせ、接続確立の待ち時間を減らす
5.1. 接続ID
- 各接続には接続IDがある
- 接続IDはエンドポイントによって選択される
- 同じ接続で同じ接続IDを複数回発行してはいけない (MUST NOT)
- 長いヘッダのパケットには接続元IDフィールドと接続先IDフィールドが含まれる
- 短いヘッダのパケットには接続先IDのみが含まれ、その長さは省略される (フィールド長はエンドポイントがすでに知っているから)
- バージョン交渉では、サーバはクライアントによって選択された接続IDをエコーして応答する
- 正しいエンドポイントにルーティングする必要がないときは、接続IDは不要で、ゼロ長の接続IDを使用する
- ただし、ゼロ長の接続IDを使用している時に、同じローカルIPアドレスとポートで多重化すると、プロトコルは正しく動かない
- 相手が非ゼロ長の接続ID を要求したときは、NEW_CONNECTION_ID を使用して接続IDを通知する
5.1.1. 接続IDの発行
- 初期接続IDは長いヘッダの接続元IDフィールドに格納される
- 初期接続IDのシーケンス番号は0
- preferred_addressトランスポートパラメータが送られたときは、初期接続IDのシーケンス番号は1
- 追加の接続IDは、NEW_CONNECTION_IDを使って送信される
- 新しく発行された接続IDのシーケンス番号は1ずつ増加する (MUST)
- クライアントがランダムに接続IDを設定したときと、Retryパケットにはシーケンス番号は割り当てない
- 接続維持時間の間、または RETIRE_CONNECTION_ID で接続IDを無効にするまで、この接続IDのパケットを受け入れなければならない (MUST)
- 発行されて無効にされていない接続IDは、有効と見なされる
- active_connection_id_limitトランスポートパラメータを使用して、保存する接続IDの数を通知する
5.1.2. 接続IDの消費と無効化
- エンドポイントはいつでも接続IDを変更できる
- 接続IDを削除するときは、RETIRE_CONNECTION_ID を送信する
- RETIRE_CONNECTION_ID の送信は、相手が NEW_CONNECTION_ID を送信して新しい接続IDへ変更することを要求する
- 「Retire Prior To」フィールドを増やした NEW_CONNECTION_ID を送信することで、相手に使用していた接続IDを無効化させることができる
- 上記の NEW_CONNECTION_ID を受信したとき、対応する接続IDを無効化し、RETIRE_CONNECTION_ID を送信する
- エンドポイントは NEW_CONNECTION_ID の確認応答を受信してから 3 PTO (Probe Timeout) 以上経過すると、接続IDを捨てることができる
5.2. パケットと接続のマッチング
- やってきたパケットは受信時に分類される
- 接続先IDが非ゼロ長のときは、パケットを既存の接続に関連付ける
- 接続先IDがゼロ長で、ローカルアドレスとポートが一致するときは、接続の一部として処理する
- 既存の接続に結びつかないパケットに対しては「Stateless Reset」を送信する
- 「Stateless Reset」によって、その接続を使用できないことがわかる
- 接続の状態と矛盾するパケットは破棄される
- 初期、再試行、バージョン交渉などの保護されず、無効なパケットは破棄される場合がある
5.2.1. クライアント側のパケット処理
- パケットの順番が変わったり、損失したりすることで、計算できていない鍵で暗号化されたパケットを受信することがある
- サポートされていないバージョンのパケットを受信した場合は、そのパケットを破棄する (MUST)
5.2.2. サーバ側のパケット処理
- サーバは、対応していないけど自身のバージョンより大きいバージョンのパケットを受信したら、バージョン交渉パケットを送信する (SHOULD)
- サーバは、バージョン交渉パケットのストームを回避するためにレート制御をする (MAY)
- 接続IDやゼロ長接続IDのときはローカルアドレスとポート番号を使用して、既存の接続と関連付ける
- 初期パケットが仕様に準拠するときは、サーバはハンドシェイクをする
- サーバは現時点で新しい接続を受け入れていないときは、SERVER_BUSY エラーを含む CONNECTION_CLOSE を送信する
- パケットが 0-RTT のとき、サーバは遅れて到着する初期パケットを見越して、このパケットを上限数までバッファリングしてもよい (MAY)
- ハンドシェイクを送信する前にクライアントから到着したハンドシェイクは無視する (SHOULD)
5.3. QUIC接続の寿命
未定 (原文ママ)
5.4. 接続に必要な操作
- クライアントの役割を実装するとき、アプリケーションは次の操作ができる必要がある
- ハンドシェイクを開始して、接続を開く
- 0-RTT の設定をする
- サーバが 0-RTT を受け取った・拒否したことを知ることができる
- サーバの役割を実装するとき、アプリケーションは次の操作ができる必要がある
- ハンドシェイクの準備をして、接続が来るのを待つ
- Early Dataに対応している場合は、クライアントに送信するTLS再開チケットにアプリケーション制御データを埋め込む
- Early Dataに対応している場合は、再開チケットからアプリケーション制御データを取得し、その情報に基づいて Early Data を拒否できるようにする
- 両方の役割を実装するとき、アプリケーションは次の操作ができる必要がある
- トランスポートパラメータの設定
- フロー制御や許可されたストリームの数などのリソース割り当ての制御
- ハンドシェイクが正常に完了したか、進行中かを識別する
- 接続が静かに終了しないようにする (PINGフレームの生成や、タイムアウトの期限が切れる前に追加のフレームを要求する)
- すぐに接続を終了できる
6. バージョン交渉
6.1. バージョン交渉パケットの送信
- クライアントが選択したバージョンをサーバは受け入れられないときは、バージョン交渉で応答する
6.2. バージョン交渉パケットの処理
- クライアントはバージョン交渉を受信したら、現在の接続試行を中断しなければならない (MUST)
6.3. 予約済みバージョンの使用
- サーバが将来新しいバージョンを使用するには、クライアントはサポートされていないバージョンを正しく処理する必要がある
- サーバはバージョン交渉パケットを生成するときに、強制的にバージョン交渉をさせることができる予約されたバージョン 0x?a?a?a?a を含める (SHOULD)
- クライアントが、バージョン 0x?a?a?a?a のパケットを送信すれば、サーバが対応しているバージョンのリストを取得するのに利用できる
7. 暗号化とトランスポートハンドシェイク
- QUICは暗号化とトランスポートハンドシェイクを組み合わせ、接続確立の遅延を最小限にする
- QUICは CRYPTO フレームで暗号化ハンドシェイクを送信する
- バージョン 0x00000001 では、QUIC-TLS で説明されている TLS を使用する
- 異なるQUICバージョンでは、異なる暗号化ハンドシェイクプロトコルが使用されている可能性がある
- QUICは暗号化ハンドシェイクデータを信頼性が高く、正しい順序で送信する
- QUICパケット保護では、次の機能を提供する
- 認証付き鍵交換
- サーバは常に認証される(サーバ証明書は常にある)
- クライアントは任意で認証される(必要に応じてクライアント証明書がある)
- 全ての接続で、独立した鍵を生成する
- 鍵を作るための情報(乱数など)は 0-RTT と 1-RTT のどちらのパケットにも使える
- 1-RTT での鍵は前方秘匿性を持つ
- トランスポートパラメータのための認証付きの値と、サーバトランスポートパラメータの機密保護
- アプリケーションプロトコルの認証付き交渉 (ALPNを使う目的のTLS)
- 認証付き鍵交換
- エンドポイントは送信する最初のパケットで明示的な輻輳通知 (Explicit Congestion Notification; ECN) を対応しているか確認できる
- CRYPTO フレームは異なるパケット番号スペースで送信できる
- 暗号化ハンドシェイクのデータは順序を保証するために CRYPTO フレームのシーケンス番号は 0 から始まる
- エンドポイントは、アプリケーションプロトコルを明示的に交渉する必要がある (MUST)
7.1. ハンドシェイクフローの例
- アドレス検証の交換が完了すると、暗号化ハンドシェイクで鍵交換をする
- 暗号化ハンドシェイクは、初期 (Initial) およびハンドシェイク (Handshake) パケットで運ばれる
- 図の読み方は、左側がクライアント、右側がサーバで、パケットは
パケットの種類[パケット番号] パケットの中身...
という感じで示されている
Client Server
Initial[0]: CRYPTO[CH] ->
Initial[0]: CRYPTO[SH] ACK[0]
Handshake[0]: CRYPTO[EE, CERT, CV, FIN]
<- 1-RTT[0]: STREAM[1, "..."]
Initial[1]: ACK[0]
Handshake[0]: CRYPTO[FIN], ACK[0]
1-RTT[0]: STREAM[0, "..."], ACK[0] ->
1-RTT[1]: STREAM[3, "..."], ACK[0]
<- Handshake[1]: ACK[0]
図3: Example 1-RTT Handshake
- 訳注:
- Initial (初期パケット) は TLS の ClientHello と ServerHello を運ぶ
- Handshake (ハンドシェイクパケット) は TLS の EncryptedExtensions, Certificate, CertificateVerify, Finished を運ぶ
- 1-RTT (アプリケーションデータ) は、TLS の ApplicationData を運ぶ
Client Server
Initial[0]: CRYPTO[CH]
0-RTT[0]: STREAM[0, "..."] ->
Initial[0]: CRYPTO[SH] ACK[0]
Handshake[0] CRYPTO[EE, FIN]
<- 1-RTT[0]: STREAM[1, "..."] ACK[0]
Initial[1]: ACK[0]
Handshake[0]: CRYPTO[FIN], ACK[0]
1-RTT[1]: STREAM[0, "..."] ACK[0] ->
1-RTT[1]: STREAM[3, "..."], ACK[1]
<- Handshake[1]: ACK[0]
図4: Example 0-RTT Handshake
7.2. 接続IDの交渉
- ハンドシェイク中、長いヘッダのパケットは接続IDを確立するために使用する
- 初期パケットやRetryパケットを受信していないクライアントが初期パケットを送信すると、接続先IDは予測不能な(ランダムな)値が設定される
- 接続先IDは少なくとも8バイト長
- 初期接続先IDは、初期パケットを保護するための鍵を導出するために使用する
- 最初に初期パケットやRetryパケットを受信したクライアントは、提供された接続元IDを、後続のパケットの接続先IDとして使用する。この場合、接続先IDは2回設定される
7.3. トランスポートパラメータ
- 接続確立中に両方のエンドポイントは、トランスポートパラメータの宣言をする
- 暗号化ハンドシェイクにエンコードされたトランスポートパラメータを含める
- 無効な値のトランスポートパラメータを受信したら TRANSPORT_PARAMETER_ERROR エラーを返す (MUST)
- 特定のパラメータを複数回送信してはいけない (MUST NOT)
- パラメータの重複した受信を TRANSPORT_PARAMETER_ERROR エラーとして扱う (SHOULD)
- Retryパケットを送信したときは、Retryパケットを検証するために original_connection_id トランスポートパラメータを含める必要がある (MUST)
7.3.1. 0-RTTのトランスポートパラメータの値
- TODO:
7.3.2. 新しいトランスポートパラメータ
- サポートしていないトランスポートパラメータは無視しなければならない (MUST)
- 無視されたときは、そのオプションのプロトコル機能は無効になる
7.4. 暗号化メッセージのバッファリング
- 実装では、順不同で受信したCRYPTOデータのバッファを維持する必要がある
- CRYPTOフレームのフロー制御はないので、相手に無限のデータをバッファさせる可能性がある
- 実装では、CRYPTOフレームで受信した少なくとも 4096 バイトの順不同なデータをバッファリングする (MUST)
- 接続中にエンドポイントはバッファサイズを一定にする必要はない
- CRYPTOフレームの全てのデータをバッファリングできないときは、これ以降に到着する全てのCRYPTOフレームを破棄するか、CRYPTO_BUFFER_EXCEEDED エラーを送信して接続を終了する
- 破棄されたCRYPTOフレームが含まれているパケットでも、トランスポートによって受信されているので、確認応答をする (MUST)
8. アドレス検証
- アドレス検証はトラフィック増幅攻撃への対策
- 増幅攻撃では、偽造された送信元にパケットが送られる
- 増幅攻撃への対策として、エンドポイントが主張するトランスポートアドレスでパケットを受信できることを確認する
- アドレス検証は、接続の確立中と接続の移行中の両方で行われる
8.1. 接続確立中のアドレス検証
8.1.1. Retryパケットを使ったアドレス検証
- トークンを含むRetryパケットを送信することで、アドレス検証する
Client Server
Initial[0]: CRYPTO[CH] ->
<- Retry+Token
Initial+Token[1]: CRYPTO[CH] ->
Initial[0]: CRYPTO[SH] ACK[1]
Handshake[0]: CRYPTO[EE, CERT, CV, FIN]
<- 1-RTT[0]: STREAM[1, "..."]
図5: Example Handshake with Retry
8.1.2. 将来の接続のアドレス検証
- 0-RTT ではアドレス検証は特に重要で、サーバは大量のデータをクライアントから受信する可能性があるため
- サーバは NEW_TOKEN フレームを用いて 0-RTT 用のトークンを送信し、0-RTT の検証時に使用する
- トークンの検証が成功したら、ハンドシェイクを続ける
- トークンは認証付き暗号で暗号化し、送信される
- DDoS攻撃への対策として、Retryパケットで送信されたトークンは、有効期限を短くする
- DDoS攻撃への対策として、NEW_TOKENフレームで送信されたトークンは、短期間で複数のアクセスを許可しないようにする必要がある (SHOULD NOT)
- 可能であればトークンは1回だけしか利用できないようにすること
8.1.3. アドレス検証トークンの整合性
- トークンは推測困難でなければならない (MUST)
- トークンには、クライアントアドレス(IPとポート)、タイムスタンプ、トークンを検証するのに必要な情報、その他の情報を含めることができる
8.2. パス検証
- パス検証は、接続の移行中に新しいアドレスは到達可能かを検証する
- PATH_CHALLENGE パケットを送信して、PATH_RESPONSE を受信するかをテストする
- 誰も使用していない接続IDを取得したいとき、NEW_CONNECTION_ID と PATH_CHALLENGE を送信して応答がないことを確認する
8.3. パス検証の開始
- ランダムなペイロードを含む PATH_CHALLENGE フレームを送信する
- パケット損失を防ぐために複数の PATH_CHALLENGE を送信できる
8.4. パス検証の応答
- PATH_CHALLENGE を受信したら PATH_RESPONSE で応答する
- PATH_RESPONSE のペイロードには受信した PATH_CHALLENGE のペイロードを格納する
8.5. 成功したパス検証
- PATH_CHALLENGE で送信したときのペイロードを含む PATH_RESPONSE を受信したら、新しいアドレスは有効とみなす
8.6. 失敗したパス検証
- 新しいアドレスは元のアドレスよりも往復時間が長いとき、新しいアドレスは無効とみなす
-
QUIC-RECOVERY によると、現在推奨されているタイムアウト時間は:
validation_timeout = max(3*PTO, 6*kInitialRtt)
9. 接続の移行
- 接続IDを使用することで、エンドポイントアドレス (IPアドレスとポート) が変更されても対応できるようになる
- ハンドシェイクが確認される前に、接続移行を開始してはいけない (MUST NOT)
- ミドルボックス (主にNAT) によってアドレス変更されたら、新しい発信IPアドレスとポートを割り当てる
- エンドポイントは相手のアドレス変更を検出したら、以前にそのアドレスを検証していない限り、パス検証を実行する必要がある (MUST)
省略…
10. 接続の終了
- 確立したQUIC接続は、次の3つの方法で終了できる
- アイドリングによるタイムアウト (idle timeout)
- 即時終了 (immediate close)
- ステートレスリセット (stateless reset)
省略…
11. エラー処理
省略…
12. パケットとフレーム
12.1. 保護されたパケット
- バージョン交渉とRetryパケットを除く全てのQUICパケットは、認証付き暗号 (AEAD) で暗号化され、機密性と完全性を持つ
- 暗号化ハンドシェイクから導出した鍵で暗号化される
12.2. パケットの結合
- 複数のQUICパケットを1つのUDPデータグラムに結合できる
12.3. パケット番号
- パケット番号は 0〜2^62-1 の範囲の整数
- この数字は暗号化鍵を作るためのナンスに利用される (TLS 1.3と同じ)
- 受信用と送信用のパケット番号を別々に管理する (TLS 1.3と同じ)
- バージョン交渉パケットとRetryパケットには、パケット番号はない
- 3つのスペースではそれぞれ別の鍵を使う
- 初期スペース : 全ての初期パケット
- ハンドシェイクスペース : 全てのハンドシェイクパケット
- アプリケーションデータスペース : 全ての0-RTT、1-RTTでの暗号化パケット
- 各スペースでパケット番号は0から始まる
- パケットを送信/受信するたびに1ずつ増やす
- パケット番号は再利用してはいけない (MUST NOT)
- パケット番号が 2^62-1 に達した場合、何も送信せずに接続を閉じなければならない (MUST)
12.4. フレームとその種類
- QUICパケットの暗号化されたペイロードを復号すると、一連のフレームから構成されている
- バージョン交渉、ステートレスリセット、Retryパケットにはフレームがない
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Frame 1 (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Frame 2 (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Frame N (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
図7: QUIC Payload
- 少なくとも1つのフレームを含める必要がある (MUST)
- 複数のフレームと複数のフレームタイプを含めることができる (MAY)
- フレームは複数のパケットにまたがることはない
- 各フレームは、フレームタイプから始まり、その後にタイプ固有のフィールドが続く
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Frame Type (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type-Dependent Fields (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
図8: Generic Frame Layout
- フレームの種類 (Frame Type) の一覧を表に示す
表3: Frame Types
値 | フレーム名 |
---|---|
0x00 | PADDING |
0x01 | PING |
0x02 - 0x03 | ACK |
0x04 | RESET_STREAM |
0x05 | STOP_SENDING |
0x06 | CRYPTO |
0x07 | NEW_TOKEN |
0x08 - 0x0f | STREAM |
0x10 | MAX_DATA |
0x11 | MAX_STREAM_DATA |
0x12 - 0x13 | MAX_STREAMS |
0x14 | DATA_BLOCKED |
0x15 | STREAM_DATA_BLOCKED |
0x16 - 0x17 | STREAMS_BLOCKED |
0x18 | NEW_CONNECTION_ID |
0x19 | RETIRE_CONNECTION_ID |
0x1a | PATH_CHALLENGE |
0x1b | PATH_RESPONSE |
0x1c - 0x1d | CONNECTION_CLOSE |
- 未知のタイプは FRAME_ENCODING_ERROR エラーとして扱う (MUST)
- 全てのQUICフレームが冪等であり、フレームを複数回受信しても副作用やエラーを起こすことはない
13. パケット化と信頼性
- QUICパケットに1つ以上のフレームを入れることができる
- QUICパケット内にできるだけ多くのフレームを入れることで、帯域の使用を最小限にできる
省略…
17. パケットの形式
17.1. パケット番号のエンコードとデコード
- パケット番号は 0〜2^62-1 の範囲の整数
- 1〜4バイトでエンコードされる
- パケット番号はTLSで暗号化される
17.2. 長いヘッダのパケット
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+
|1|1|T T|X X X X|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Version (32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| DCID Len (8) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Connection ID (0..160) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SCID Len (8) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Connection ID (0..160) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- Header Form: 長いヘッダの最上位ビットは1
- 固定ビット: その次のビットは必ず1
- 長いパケットのタイプ (T) : 表5のいずれか
- タイプ固有のビット (X) :
- Version : QUICのバージョン
- 接続先ID長 (DCID Len) : 8ビット符号なし整数
- 接続先ID (Destination Connection ID) :
- 接続元ID長 (SCID Len) : 8ビット符号なし整数
- 接続元ID (Source Connection ID) :
表5: Long Header Packet Types
Type | Name |
---|---|
0x0 | Initial |
0x1 | 0-RTT |
0x2 | Handshake |
0x3 | Retry |
- 予約されたビット (R) : 必ず 0 にする (MUST)
- パケット番号長 (P) :
- 長さ (Length) : ペイロードの長さ
- パケット番号 (Packet Number) : 1〜4バイト
バージョン交渉パケット
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+
|1| Unused (7) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Version (32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| DCID Len (8) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Connection ID (0..2040) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SCID Len (8) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Connection ID (0..2040) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Supported Version 1 (32) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| [Supported Version 2 (32)] ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| [Supported Version N (32)] ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
初期パケット (Initial Packet)
- クライアントやサーバによって送信される最初のCRYPTOフレームを運ぶ
- 逆方向に、その確認応答 ACK を運ぶ
+-+-+-+-+-+-+-+-+
|1|1| 0 |R R|P P|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Version (32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| DCID Len (8) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Connection ID (0..160) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SCID Len (8) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Connection ID (0..160) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Token Length (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Token (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Packet Number (8/16/24/32) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Payload (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0-RTT
+-+-+-+-+-+-+-+-+
|1|1| 1 |R R|P P|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Version (32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| DCID Len (8) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Connection ID (0..160) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SCID Len (8) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Connection ID (0..160) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Packet Number (8/16/24/32) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Payload (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
ハンドシェイクパケット
+-+-+-+-+-+-+-+-+
|1|1| 2 |R R|P P|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Version (32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| DCID Len (8) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Connection ID (0..160) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SCID Len (8) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Connection ID (0..160) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Packet Number (8/16/24/32) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Payload (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Retryパケット
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+
|1|1| 3 | Unused|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Version (32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| DCID Len (8) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Connection ID (0..160) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SCID Len (8) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Connection ID (0..160) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ODCID Len (8) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Original Destination Connection ID (0..160) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Retry Token (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17.3. 短いヘッダのパケット
- 短いヘッダは、暗号化鍵を共有した後に使う
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+
|0|1|S|R|R|K|P P|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Connection ID (0..160) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Packet Number (8/16/24/32) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Protected Payload (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- Spin Bit (S) : 遅延監視機能 (OPTIONAL)
- Key Phase (K) : パケット暗号化鍵を識別するのに使う
- Packet Number Length (P)
18. トランスポートパラメータのエンコード
- トランスポートパラメータの一覧
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Length (16) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Transport Parameter 1 (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Transport Parameter 2 (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Transport Parameter N (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- 各トランスポートパラメータの構造
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Transport Parameter ID (16) | Transport Param Length (16) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Transport Parameter Value (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
18.2. トランスポートパラメータの定義
- original_connection_id (0x0000):
- idle_timeout (0x0001):
- stateless_reset_token (0x0002):
- max_packet_size (0x0003):
- initial_max_data (0x0004):
- initial_max_stream_data_bidi_local (0x0005):
- initial_max_stream_data_bidi_remote (0x0006):
- initial_max_stream_data_uni (0x0007):
- initial_max_streams_bidi (0x0008):
- initial_max_streams_uni (0x0009):
- ack_delay_exponent (0x000a):
- max_ack_delay (0x000b):
- disable_active_migration (0x000c):
- preferred_address (0x000d):
19. フレームの種類と形式
PADDINGフレーム
PINGフレーム
ACKフレーム
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Largest Acknowledged (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ACK Delay (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ACK Range Count (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| First ACK Range (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ACK Ranges (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| [ECN Counts] ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
ACK範囲 (ACK Ranges)
- 複数のACKを一つのACKにまとめる
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| [Gap (i)] ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| [ACK Range (i)] ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| [Gap (i)] ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| [ACK Range (i)] ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| [Gap (i)] ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| [ACK Range (i)] ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- Gap : 確認応答しないパケットの数。次のACK範囲の最小値よりも1つ小さい値?
- ACK Range : 確認応答するパケットの数。ACK範囲の最大値?
ECNカウント
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ECT(0) Count (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ECT(1) Count (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ECN-CE Count (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
RESET_STREAMフレーム
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Stream ID (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Application Error Code (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Final Size (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
STOP_SENDINGフレーム
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Stream ID (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Application Error Code (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
CRYPTOフレーム
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Offset (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Crypto Data (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
NEW_TOKENフレーム
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Token Length (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Token (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
(各種フレームの説明が続く…)
20. トランスポートエラーコード
- NO_ERROR (0x0):
- INTERNAL_ERROR (0x1):
- SERVER_BUSY (0x2):
- FLOW_CONTROL_ERROR (0x3):
- STREAM_LIMIT_ERROR (0x4):
- STREAM_STATE_ERROR (0x5):
- FINAL_SIZE_ERROR (0x6):
- FRAME_ENCODING_ERROR (0x7):
- TRANSPORT_PARAMETER_ERROR (0x8):
- PROTOCOL_VIOLATION (0xA):
- CRYPTO_BUFFER_EXCEEDED (0xD):
- CRYPTO_ERROR (0x1XX):
21. セキュリティの考慮事項
- Handshake Denial of Service (ハンドシェイクDoS攻撃)
- 暗号化ハンドシェイクが完了すると、認証されていないほとんどパケットを破棄する
- 認証されていないICMPパケットを受け入れる可能性があるが、これらのパケットの使用は制限されている
- Amplification Attack (アンプ攻撃)
- 攻撃者は、サーバからアドレス検証トークンを受信し、自身のIPアドレスを解放する
- その後で、スプーリングしたIPアドレスによって 0-RTT 接続を開始する
- これにより、攻撃対象者へ最初の輻輳ウィンドウの値を送信させることができる
- Optimistic ACK Attack (楽観的なACK攻撃)
- パケットを受信していないのにACKを返すことで、帯域を超えた速度での通信を引き起こす
- 対策としては、パケットを送信するときに、パケット番号を飛び飛びにすることで検知できる
- Slowloris Attacks (スローロリス攻撃)
- Slowlorisはコネクションが切れない程度に時間を置きながら少しずつパケットを送信する攻撃
- 攻撃者は受信パケットに対して、あたかも高い損失率であるかのように振る舞うことも、この攻撃に含まれる
- QUICにおいては、サーバが許可するクライアントの最大接続数を増やす、一つのIPアドレスが許可する接続の数を制限する、最小転送量に制限を設ける、接続を維持できる時間を制限する、などの対策がある
- Stream Fragmentation and Reassembly Attacks (ストリーム分割と再構築による攻撃)
- パケットをフラグメントに分割して、意図的に複数のストリームを使って送信することで、相手のメモリ使用量を増やす
- QUICにおいては、メモリのオーバコミットの回避、追跡データのサイズ制限、STREAMフレームの再構築を遅くすること、などの対策がある
- Stream Commitment Attack (ストリーム確保攻撃)
- 大量のストリームを開いて、相手のメモリを使い果たす攻撃
- Peer Denial of Service (ペアDoS攻撃)
- 状態の変更や取り消しによって、相手の処理リソースを消費させる攻撃
- Explicit Congestion Notification Attacks (明示的な輻輳通知攻撃)
- ミドルボックスがECNの値を改ざんして、送信者のレートを変更する攻撃
- Stateless Reset Oracle (ステートレスリセットオラクル攻撃)
- ステートレスリセットを使った、TCPリセットと類似するような攻撃
- Version Downgrade (バージョンダウングレード攻撃)
- バージョン交渉中にミドルボックスがバージョンを下げる攻撃
- Targeted Attacks by Routing (ルーティングによる標的型攻撃)