QUIC-TLS
RFC 9001 Using TLS to Secure QUIC
https://tex2e.github.io/rfc-translater/html/rfc9001.html
https://www.rfc-editor.org/rfc/rfc9001.html
https://datatracker.ietf.org/doc/html/rfc9001
HTTP/3のベースになるQUICとTLSの組み合わせ
TLSを使用してQUICを保護する
概要
このドキュメントでは、トランスポート層セキュリティ(TLS)を使用してQUICを保護する方法について説明します。
1. はじめに
このドキュメントでは、QUIC QUIC-TRANSPORT が TLS TLS13 を用いてどのようにセキュリティ保護されるかについて説明します。
TLS 1.3 は、以前のバージョンと比較して、接続確立のレイテンシを大幅に改善します。パケットロスがない場合、ほとんどの新規接続は1回のラウンドトリップで確立およびセキュリティ保護されます。同じクライアントとサーバー間の後続の接続では、クライアントは多くの場合、アプリケーションデータを即座に送信できます。つまり、ラウンドトリップゼロのセットアップで済みます。このドキュメントでは、TLS が QUIC のセキュリティコンポーネントとしてどのように機能するかについて説明します。
2. 表記規則
本文書におけるキーワード「MUST(しなければならない)」、「MUST NOT(してはならない)」、「REQUIRED(必須)」、「SHALL(する)」、「SHALL NOT(するべきではない)」、「SHOULD(すべきである)」、「SHOULD NOT(すべきではない)」、「RECOMMENDED(推奨される)」、「NOT RECOMMENDED(推奨されない)」、「MAY(してもよい)」、「OPTIONAL(選択できる)」は、ここで示すようにすべて大文字で表記されている場合に限り、BCP 14 RFC 2119 RFC 8174 の規定に従って解釈されます。
本文書では、QUIC-TRANSPORT で確立された用語を使用します。簡潔にするため、TLS という略語は TLS 1.3 を指しますが、より新しいバージョンを使用することもできます。詳細はセクション 4.2 を参照してください。
2.1. TLS の概要
TLS は、2 つのエンドポイントに、信頼できない媒体(インターネットなど)を介した通信手段を確立する手段を提供します。TLS は、ピアの認証を可能にし、エンドポイント間で交換されるメッセージの機密性と整合性を保護します。
TLS は内部的に階層化プロトコルであり、その構造は図 1 に示されています。
code:TLS レイヤー
+---------------+---------+-----------------+-------+
コンテンツ | | | アプリケーション | |
レイヤー | ハンドシェイク | アラート | データ | ... |
| | | | |
+---------------+---------+-----------------+-------+
レコード | |
レイヤー | レコード |
| |
+---------------------------------------------------+
図 1: TLS レイヤー
コンテンツ層の各メッセージ(ハンドシェイク、アラート、アプリケーションデータなど)は、レコード層によって一連の型付き TLS レコードとして伝送されます。レコードは個別に暗号的に保護され、信頼性の高いトランスポート(通常は TCP)を介して送信されます。これにより、順序付けと配信の保証が提供されます。
TLS 認証による鍵交換は、クライアントとサーバーの 2 つのエンドポイント間で行われます。クライアントが鍵交換を開始し、サーバーが応答します。鍵交換が正常に完了すると、クライアントとサーバーの両方が秘密鍵で合意します。TLS は、有限体または楕円曲線 ((EC)DHE) 鍵交換における事前共有鍵 (PSK) と Diffie-Hellman の両方をサポートしています。PSK は Early Data (0-RTT) の基盤であり、後者は (EC)DHE 鍵が破棄された際に Forward Secrecy (FS) を提供します。これら 2 つのモードを組み合わせることで、PSK を認証に使用しながら前方秘匿性を確保できます。
TLS ハンドシェイクが完了すると、クライアントはサーバーの ID を学習して認証し、サーバーはオプションでクライアントの ID を学習して認証できます。TLS は、サーバーとクライアントの両方で X.509 RFC 5280 証明書ベースの認証をサポートしています。PSK 鍵交換が使用される場合 (再開時など)、PSK の知識がピアの認証に使用されます。
TLS 鍵交換は攻撃者による改ざんに耐性があり、どちらの参加ピアからも制御できない共有秘密を生成します。
TLS は、QUIC にとって重要な 2 つの基本的なハンドシェイク モードを提供します。
完全な 1-RTT ハンドシェイクでは、クライアントは 1 回のラウンドトリップ後にアプリケーション データを送信でき、サーバーはクライアントからの最初のハンドシェイク メッセージを受信するとすぐに応答します。
0-RTT ハンドシェイクでは、クライアントはサーバーについて以前に学習した情報を使用して、アプリケーション データをすぐに送信します。このアプリケーションデータは攻撃者によって再生される可能性があるため、再生された場合に望ましくない影響を引き起こす可能性のあるアクションを開始する可能性のある命令を伝送するには、0-RTT は適していません。
0-RTT アプリケーションデータを使用した簡略化された TLS ハンドシェイクを図 2 に示します。
code:TLS Handshake with 0-RTT
クライアント サーバー
ClientHello
(0-RTT アプリケーションデータ) -------->
ServerHello
{EncryptedExtensions}
{Finished}
<-------- アプリケーションデータ
{Finished} -------->
アプリケーションデータ <-------> アプリケーションデータ
() は、早期データ (0-RTT) 鍵で保護されたメッセージを示します。
{} は、ハンドシェイク鍵を使用して保護されたメッセージを示します。
[] は、アプリケーションデータ (1-RTT) 鍵を使用して保護されたメッセージを示します。
図 2: 0-RTT を使用した TLS ハンドシェイク
図 2 では、QUIC では使用されない EndOfEarlyData メッセージが省略されています。セクション8.3を参照してください。同様に、ChangeCipherSpecメッセージもKeyUpdateメッセージもQUICでは使用されません。ChangeCipherSpecはTLS 1.3では冗長です。セクション8.4を参照してください。QUICには独自の鍵更新メカニズムがあります。セクション6を参照してください。
データは複数の暗号化レベルを使用して保護されます。
初期鍵
初期データ(0-RTT)鍵
ハンドシェイク鍵
アプリケーションデータ(1-RTT)鍵
アプリケーションデータは初期データレベルとアプリケーションデータレベルでのみ使用できます。ハンドシェイクメッセージとアラートメッセージはどのレベルでも使用できます。
0-RTTハンドシェイクは、クライアントとサーバーが以前に通信したことがある場合に使用できます。1-RTTハンドシェイクでは、クライアントはサーバーから送信されたすべてのハンドシェイクメッセージを受信するまで、保護されたアプリケーションデータを送信できません。
3. プロトコルの概要
QUIC QUIC-TRANSPORT は、パケットの機密性と完全性の保護を担います。そのため、TLS ハンドシェイク TLS13 から生成された鍵を使用しますが、TCP のように QUIC 上で TLS レコードを伝送するのではなく、TLS ハンドシェイクとアラートメッセージは QUIC トランスポート上で直接伝送され、図 3 に示すように TLS レコード層の責任を QUIC が引き継ぎます。
code:図3 QUIC層
+--------------+--------------+ +-------------+
| TLS | TLS | | QUIC |
| ハンドシェイク| Alerts | | Applications|
| | | | (h3, etc.) |
+--------------+--------------+-+-------------+
| |
| QUIC Transport |
| (streams, reliability, congestion, etc.) |
| |
+---------------------------------------------+
| |
| QUIC Packet Protection |
| |
+---------------------------------------------+
QUICは、セキュリティとパフォーマンスに不可欠な認証およびパラメータのネゴシエーションにおいてTLSに依存しています。
厳密な階層構造ではなく、これら2つのプロトコルは連携して動作します。QUICはTLSハンドシェイクを使用し、TLSはQUICが提供する信頼性、順序付き配信、およびレコードレイヤーを利用します。
TLSとQUICコンポーネント間の相互作用は、大きく分けて2つあります。
TLSコンポーネントはQUICコンポーネントを介してメッセージを送受信し、QUICはTLSに対して信頼性の高いストリーム抽象化を提供します。
TLSコンポーネントは、QUICコンポーネントに対して一連の更新情報を提供します。これには、(a)インストールする新しいパケット保護キー、(b)ハンドシェイクの完了、サーバー証明書などの状態変化が含まれます。
図4は、これらの相互作用をより詳細に示しており、特にQUICパケット保護について言及しています。
code:図4 QUICとTLSの相互作用
+------------+ +------------+
| |<-- ハンドシェイク Messages --->| |
| |<- Validate 0-RTT Parameters ->| |
| |<--------- 0-RTT Keys ---------| |
| QUIC |<------ ハンドシェイク鍵 -------| TLS |
| |<--------- 1-RTT Keys ---------| |
| |<------- Handshake Done -------| |
+------------+ +------------+
| ^
| Protect | Protected
v | Packet
+------------+
| QUIC |
| Packet |
| Protection |
+------------+
TCP上のTLSとは異なり、QUICアプリケーションはデータを送信する際にTLSアプリケーションデータレコードを使用しません。代わりに、QUIC STREAMフレームまたはその他のフレームタイプとして送信し、それらはQUICパケットに収められます。
4. TLSメッセージの伝送
QUICはTLSハンドシェイクデータをCRYPTOフレームで伝送します。各CRYPTOフレームは、オフセットと長さで識別される連続したハンドシェイクデータブロックで構成されます。これらのフレームはQUICパケットにパッケージ化され、現在の暗号化レベルで暗号化されます。TCP上のTLSと同様に、TLSハンドシェイクデータがQUICに配信されると、そのデータを確実に配信するのはQUICの責任となります。TLSによって生成される各データチャンクは、TLSが現在使用している鍵セットに関連付けられます。QUICがそのデータを再送信する必要がある場合、TLSが既に新しい鍵に更新されている場合でも、同じ鍵を使用しなければなりません。
各暗号化レベルはパケット番号空間に対応します。使用されるパケット番号空間によって、フレームの意味が決まります。一部のフレームは、異なるパケット番号空間では使用が禁止されています。QUIC-TRANSPORTのセクション12.5を参照してください。
パケットはネットワーク上で順序が変更される可能性があるため、QUICは表1に示すように、パケットタイプを使用して特定のパケットを保護するために使用されたキーを示します。異なるタイプのパケットを送信する必要がある場合、エンドポイントはそれらを結合パケットを使用して同じUDPデータグラムで送信する必要があります。
table:表1 パケット型による暗号化鍵
Packet Type 暗号化鍵 PN Space
初期 初期シークレット 初期
0-RTT Protected 0-RTT アプリケーションdata
ハンドシェイク ハンドシェイク ハンドシェイク
Retry Retry N/A
Version Negotiation N/A N/A
Short Header 1-RTT アプリケーションdata
QUIC-TRANSPORT の第 17 節では、様々な暗号化レベルのパケットがハンドシェイク プロセスにどのように組み込まれるかを示しています。
4.1. TLS へのインターフェース
図 4 に示すように、QUIC から TLS へのインターフェースは、主に次の 4 つの機能で構成されています。
ハンドシェイク メッセージの送受信
再開されたセッションから保存されたトランスポート状態とアプリケーション状態を処理し、0-RTT データを生成または受信できるかどうかを判定する
鍵の再生成 (送信と受信の両方)
ハンドシェイク状態の更新
TLS を設定するために、追加の機能が必要になる場合があります。特に、QUIC と TLS は、証明書の検証 RFC5280 など、ピアの認証情報の検証をどちらが担当するかについて合意する必要があります。
4.1.1. ハンドシェイクの完了
このドキュメントでは、TLS スタックがハンドシェイクの完了を報告した時点で、TLS ハンドシェイクが完了したとみなされます。これは、TLS スタックが Finished メッセージを送信し、ピアの Finished メッセージを検証した時点で発生します。ピアからのFinishedメッセージを検証することで、エンドポイントは以前のハンドシェイクメッセージが改ざんされていないことを保証できます。ただし、ハンドシェイクは両方のエンドポイントで同時に完了するわけではないことに注意してください。したがって、ハンドシェイクの完了を前提とする要件は、対象となるエンドポイントの視点に依存します。
4.1.2. ハンドシェイクの確認
本ドキュメントでは、TLSハンドシェイクは、サーバー側でハンドシェイクが完了した時点で確認されたとみなされます。サーバーは、ハンドシェイク完了後直ちにHANDSHAKE_DONEフレームを送信しなければなりません。クライアント側では、HANDSHAKE_DONEフレームを受信した時点でハンドシェイクが確認されたとみなされます。
さらに、クライアントは、1-RTTパケットに対する確認応答を受信した時点でハンドシェイクが確認されたとみなすこともできます。これは、1-RTTキーで送信されたパケット番号の最小値を記録し、受信した1-RTT ACKフレームのLargest Acknowledgedフィールドと比較することで実現できます。後者が前者以上であれば、ハンドシェイクは確認されたとみなされます。
4.1.3. ハンドシェイクメッセージの送受信
ハンドシェイクを実行するために、TLSはハンドシェイクメッセージの送受信機能に依存しています。このインターフェースには、QUICがハンドシェイクメッセージを要求する機能と、QUICがハンドシェイクメッセージを構成するバイト列を提供する機能という、2つの基本的な機能があります。
ハンドシェイクを開始する前に、QUICはTLSに、伝送したいトランスポートパラメータ(8.2項参照)を提供します。
QUICクライアントは、TLSにハンドシェイクバイト列を要求することでTLSを開始します。クライアントは、最初のパケットを送信する前にハンドシェイクバイト列を取得します。QUICサーバーは、クライアントのハンドシェイクバイト列をTLSに提供することでプロセスを開始します。
エンドポイントのTLSスタックは、常に送信暗号化レベルと受信暗号化レベルを保持しています。TLS暗号化レベルによって、QUICパケットの種類とデータ保護に使用される鍵が決まります。
各暗号化レベルは異なるバイト列に対応しており、これはCRYPTOフレームでピアに確実に送信されます。TLSがハンドシェイクバイトを送信する場合、それらは現在の暗号化レベルのハンドシェイクバイトに追加されます。暗号化レベルによって、生成されたCRYPTOフレームが格納されるパケットの種類が決まります(表1参照)。
4つの暗号化レベルが使用され、初期パケット、0-RTTパケット、ハンドシェイクパケット、および1-RTTパケットの鍵が生成されます。CRYPTOフレームは、これらのレベルのうち3つ(0-RTTレベルを除く)でのみ伝送されます。これらの4つのレベルは、3つのパケット番号空間に対応しています。初期暗号化パケットとハンドシェイク暗号化パケットはそれぞれ独自の空間を使用し、0-RTTパケットと1-RTTパケットはアプリケーションデータパケットの番号空間を使用します。
QUICは、TLSハンドシェイクレコードの保護されていない内容をCRYPTOフレームの内容として使用します。QUICではTLSレコードの保護は使用されません。 QUICはCRYPTOフレームをQUICパケットに組み立て、QUICパケット保護によって保護します。
QUIC CRYPTOフレームにはTLSハンドシェイクメッセージのみが含まれます。TLSアラートはQUIC CONNECTION_CLOSEエラーコードに変換されます(セクション4.8を参照)。TLSアプリケーションデータやその他のコンテンツタイプは、どの暗号化レベルにおいてもQUICで伝送できません。TLSスタックからこれらのデータを受信した場合、エラーとなります。
エンドポイントがネットワークからCRYPTOフレームを含むQUICパケットを受信すると、以下の処理が行われます。
パケットが現在TLSで受信している暗号化レベルを使用している場合、通常どおりデータを入力フローにシーケンス化します。STREAMフレームと同様に、オフセットを使用してデータシーケンス内の適切な位置を特定します。この処理の結果、新しいデータが利用可能になった場合、そのデータは順番にTLSに渡されます。
パケットが以前にインストールされた暗号化レベルからのものである場合、そのフローで以前に受信したデータの末尾を超えるデータを含んではなりません。実装は、この要件に違反した場合、PROTOCOL_VIOLATIONタイプの接続エラーとして処理する必要があります。
パケットが新しい暗号化レベルからのものである場合、TLSによる後処理のために保存されます。TLSがこの暗号化レベルからの受信に移行すると、保存されたデータをTLSに提供できます。TLSがより高い暗号化レベルの鍵を提供する際に、TLSが処理していない以前の暗号化レベルのデータが存在する場合、これはPROTOCOL_VIOLATIONタイプの接続エラーとして処理する必要があります。
TLSに新しいデータが提供されるたびに、TLSは新しいハンドシェイクバイトを要求します。受信したハンドシェイクメッセージが不完全な場合、または送信するデータがない場合、TLSはバイトを提供しないことがあります。
CRYPTOフレームの内容は、TLSによって段階的に処理されるか、完全なメッセージまたはフライトが利用可能になるまでバッファリングされます。TLSは、順番通りに到着したハンドシェイクバイトのバッファリングを担当します。QUICは、順不同で到着したハンドシェイクバイト、またはまだ準備できていない暗号化レベルのハンドシェイクバイトのバッファリングを担当します。QUICはCRYPTOフレームのフロー制御手段を提供しません。QUIC-TRANSPORTのセクション7.5を参照してください。
TLSハンドシェイクが完了すると、TLSが送信する必要のある最終ハンドシェイクバイトとともに、その旨がQUICに通知されます。この段階で、ピアがハンドシェイク中に通知したトランスポートパラメータが認証されます。セクション8.2を参照してください。
ハンドシェイクが完了すると、TLSはパッシブ状態になります。 TLSはピアからデータを受信し、それに応じて応答することはできますが、アプリケーションまたはQUICから明示的に要求されない限り、追加のデータを送信する必要はありません。データを送信する理由の一つとして、サーバーがクライアントに追加または更新されたセッションチケットを提供したい場合が挙げられます。
ハンドシェイクが完了すると、QUICはCRYPTOストリームで到着したデータのみをTLSに提供する必要があります。ハンドシェイク時と同様に、受信したデータを提供した後、TLSに対して新しいデータの要求が行われます。
4.1.4. 暗号化レベルの変更
TLSは、特定の暗号化レベルの鍵が利用可能になると、QUICに対し、その暗号化レベルの鍵の読み書きが可能になったことを通知します。
新しい鍵が利用可能になるのは、常にTLSへの入力データの提供によるものです。TLSは、クライアントによる初期化後、または新しいハンドシェイクデータが提供された後にのみ、新しい鍵を提供します。
ただし、TLSの実装によっては、処理の一部を非同期で実行する場合があります。特に、証明書の検証処理には時間がかかることがあります。TLS処理が完了するまでの間、エンドポイントは、まだ利用できない鍵で処理される可能性のある受信パケットをバッファリングする必要があります。これらのパケットは、TLSから鍵が提供された後に処理されます。エンドポイントは、この間も処理可能なパケットへの応答を継続する必要があります。
入力データの処理後、TLSはハンドシェイクバイト、新しい暗号化レベルの鍵、またはその両方を生成する場合があります。
TLSは、新しい暗号化レベルが利用可能になると、QUICに以下の3つの項目を提供します。
秘密(secret)
認証付き暗号化(AEAD)関数
鍵導出関数(KDF)
これらの値は、TLSがネゴシエートした値に基づいており、QUICがパケットおよびヘッダー保護鍵を生成するために使用されます。詳細は、セクション5およびセクション5.4を参照してください。
0-RTTが利用可能な場合、クライアントがTLS ClientHelloメッセージを送信するか、サーバーがそのメッセージを受信した後に準備が整います。TLSスタックは、QUICクライアントに最初のハンドシェイクバイトを提供した後、0-RTT鍵への変更を通知する場合があります。サーバー側では、ClientHelloメッセージを含むハンドシェイクバイトを受信した後、TLSサーバーが0-RTT鍵が利用可能になったことを通知する場合があります。
TLSは一度に1つの暗号化レベルしか使用しませんが、QUICは複数のレベルを使用できます。例えば、エンドポイントは、Finishedメッセージ(ハンドシェイク暗号化レベルのCRYPTOフレームを使用)を送信した後、STREAMデータ(1-RTT暗号化)を送信できます。Finishedメッセージが失われた場合、エンドポイントはハンドシェイク暗号化レベルを使用して失われたメッセージを再送信します。パケットの順序変更や損失が発生すると、QUICは複数の暗号化レベルでパケットを処理する必要が生じます。ハンドシェイク中は、TLSで現在使用されている暗号化レベルよりも高いレベルと低いレベルの両方の暗号化レベルでパケットを処理する必要が生じる可能性があります。
特に、サーバー実装は、0-RTT暗号化レベルと同時にハンドシェイク暗号化レベルのパケットを読み取ることができなければなりません。クライアントは、ハンドシェイク鍵で保護されたACKフレームを0-RTTデータと混在させる可能性があり、サーバーは失われたハンドシェイクパケットを検出するためにこれらの確認応答を処理する必要があります。
また、QUICは、通常TLS実装では利用できない鍵へのアクセスも必要とします。例えば、クライアントは、その暗号化レベルでCRYPTOフレームを送信する準備が整う前に、ハンドシェイクパケットを確認する必要があるかもしれません。そのため、TLSは、QUICが自身のために鍵を生成する前に、QUICに鍵を提供する必要があります。
4.1.5. TLSインターフェースの概要
図5は、クライアントとサーバー双方におけるQUICとTLS間のデータ交換の概要を示しています。実線の矢印はハンドシェイクデータを含むパケットを示し、破線の矢印はアプリケーションデータの送信先を示しています。各矢印には、その送信に使用される暗号化レベルが示されています。
code:図5 QUICとTLS間の相互作用の概要
Client Server
====== ======
Get Handshake
Initial ------------->
Install tx 0-RTT keys
0-RTT - - - - - - - ->
Handshake Received
Get Handshake
<------------- Initial
Install rx 0-RTT keys
Install Handshake keys
Get Handshake
<----------- Handshake
Install tx 1-RTT keys
<- - - - - - - - 1-RTT
Handshake Received (Initial)
Install Handshake keys
Handshake Received (Handshake)
Get Handshake
Handshake ----------->
Handshake Complete
Install 1-RTT keys
1-RTT - - - - - - - ->
Handshake Received
Handshake Complete
Handshake Confirmed
Install rx 1-RTT keys
<--------------- 1-RTT
(HANDSHAKE_DONE)
Handshake Confirmed
クライアント サーバー
====== ======
ハンドシェイクの取得
初期 ------------->
送信 0-RTT キーのインストール
0-RTT - - - - - - - ->
ハンドシェイク受信
ハンドシェイクの取得
<------------- 初期
受信 0-RTT キーのインストール
ハンドシェイクキーのインストール
ハンドシェイクの取得
<----------- ハンドシェイク
送信 1-RTT キーのインストール
<- - - - - - - - 1-RTT
ハンドシェイク受信(初期)
ハンドシェイクキーのインストール
ハンドシェイク受信(ハンドシェイク)
ハンドシェイクの取得
ハンドシェイク ----------->
ハンドシェイク完了
1-RTT キーのインストール
1-RTT - - - - - - - ->
ハンドシェイク受信しました
ハンドシェイク完了
ハンドシェイク確認済み
rx 1-RTTキーをインストール
<--------------- 1-RTT
(HANDSHAKE_DONE)
ハンドシェイク確認済み
図5は、単一の「フライト」を構成する複数のパケットが個別に処理される様子を示しており、受信メッセージによって異なるアクションがトリガーされることがわかります。これは、異なる暗号化レベルでハンドシェイクメッセージを取得するために、複数の「Get Handshake」呼び出しが行われていることを示しています。受信パケットの処理が完了すると、新しいハンドシェイクメッセージが要求されます。
図5は、単純なハンドシェイク交換の一例として考えられる構造を示しています。実際の処理は、エンドポイントの実装構造とパケットの到着順序によって異なります。実装によっては、使用する操作の数や実行順序が異なる場合があります。
4.2. TLSバージョン
このドキュメントでは、QUICにおけるTLS 1.3 TLS13 の使用方法について説明します。
実際には、TLSハンドシェイクによって使用するTLSバージョンがネゴシエートされます。両方のエンドポイントがそのバージョンをサポートしている場合、TLS 1.3よりも新しいバージョンがネゴシエートされる可能性があります。QUICで使用されるTLS 1.3の機能が新しいバージョンでもサポートされている限り、これは許容されます。
クライアントはTLS 1.3より古いバージョンを提供してはなりません。TLSの実装設定が不適切だと、TLS 1.2またはそれより古いバージョンのTLSがネゴシエートされる可能性があります。エンドポイントは、TLS 1.3より古いバージョンのTLSがネゴシエートされた場合、接続を終了しなければなりません。
4.3. ClientHelloのサイズ
クライアントからの最初のInitialパケットには、最初の暗号化ハンドシェイクメッセージ(TLSの場合はClientHello)の先頭または全体が含まれます。サーバーは、新しいQUIC接続を受け入れるかどうかを判断するために、ClientHello全体を解析する必要がある場合があります(例えば、Server Name Identification(SNI)やApplication-Layer Protocol Negotiation(ALPN)などの拡張機能にアクセスするため)。ClientHelloが複数のInitialパケットにまたがる場合、サーバーは最初に受信したフラグメントをバッファリングする必要がありますが、クライアントのアドレスがまだ検証されていない場合は、過剰なリソースを消費する可能性があります。これを回避するため、サーバーは、検証済みのアドレスを持つクライアントからの ClientHello メッセージの一部のみをバッファリングするために、再試行機能(QUIC-TRANSPORT のセクション 8.1 を参照)を使用する場合があります。
QUIC パケットとフレーミングにより、ClientHello メッセージには少なくとも 36 バイトのオーバーヘッドが追加されます。クライアントが送信元接続 ID フィールドを 0 バイトより長く設定した場合、このオーバーヘッドはさらに増加し​​ます。オーバーヘッドには、トークンや 8 バイトより長い宛先接続 ID は含まれません。これらは、サーバーが再試行パケットを送信する場合に必要となる場合があります。
一般的な TLS ClientHello は、1200 バイトのパケットに容易に収まります。しかし、QUIC によって追加されるオーバーヘッドに加えて、この制限を超える可能性のある要因がいくつかあります。セッション チケットが大きい場合、複数の鍵共有または鍵共有が大きい場合、サポートされている暗号、署名アルゴリズム、バージョン、QUIC トランスポート パラメータ、その他の交渉可能なパラメータや拡張機能のリストが長い場合、このメッセージのサイズが大きくなる可能性があります。
サーバー側では、接続IDやトークンに加えて、TLSセッションチケットのサイズもクライアントの接続効率に影響を与える可能性があります。これらの値を最小限に抑えることで、クライアントが最初のInitialパケットにClientHelloメッセージ全体を収めることができる可能性が高まります。
TLS実装では、ClientHelloがInitialパケットを伝送するデータグラムに関するQUICの要件を満たすのに十分な大きさであることを保証する必要はありません。QUIC-TRANSPORTのセクション14.1を参照してください。QUIC実装では、データグラムが十分な大きさになるように、パディングフレームまたはパケット結合を使用します。
4.4. ピア認証
認証要件は、使用するアプリケーションプロトコルによって異なります。TLSはサーバー認証を提供し、サーバーがクライアント認証を要求することを可能にします。
クライアントは、サーバーの身元を認証する必要があります。これは通常、サーバーの身元が証明書に含まれていること、およびその証明書が信頼できる機関によって発行されていることを検証することによって行われます(例えば、RFC2818を参照)。
注:サーバーが認証のために証明書を提供する場合、証明書チェーンのサイズは大量のバイトを消費する可能性があります。QUICでは、サーバーはクライアントアドレスを検証する前に受信したバイトごとに3バイトしか送信できないため、証明書チェーンのサイズを制御することがパフォーマンスにとって非常に重要です(QUIC-TRANSPORTのセクション8.1を参照)。証明書チェーンのサイズは、名前または拡張子の数を制限する、ECDSAなどの小さな公開鍵表現を持つ鍵を使用する、または証明書圧縮COMPRESSを使用することによって管理できます。
サーバーは、ハンドシェイク中にクライアントに認証を要求する場合があります。クライアントが要求された認証に失敗した場合、サーバーは接続を拒否することができます。クライアント認証の要件は、アプリケーションプロトコルと展開方法によって異なります。
サーバーは、ハンドシェイク後のクライアント認証(TLS13の4.6.2項で定義)を使用してはなりません。QUICが提供する多重化機能により、クライアントは証明書要求と、それをトリガーしたアプリケーションレベルのイベントを関連付けることができないためです(HTTP2-TLS13を参照)。より具体的には、サーバーはハンドシェイク後のTLS CertificateRequestメッセージを送信してはならず、クライアントはこのようなメッセージを受信した場合、PROTOCOL_VIOLATIONタイプの接続エラーとして処理しなければなりません。
4.5. セッション再開
QUICは、TLS 1.3のセッション再開機能を使用できます。これは、ハンドシェイク完了後にCRYPTOフレームにNewSessionTicketメッセージを付加することで実現されます。セッション再開は、0-RTTを実現するために使用でき、0-RTTが無効になっている場合にも使用できます。
セッション再開を使用するエンドポイントは、再開接続を作成する際に、現在の接続に関する情報を記憶しておく必要がある場合があります。TLSでは、一部の情報を保持することが求められます。TLS13のセクション4.6.1を参照してください。QUIC自体は、0-RTTが併用されない限り、接続再開時に状態が保持されることに依存しません。QUIC-TRANSPORTのセクション7.4.1およびセクション4.6.1を参照してください。アプリケーションプロトコルは、再開接続間で保持される状態に依存する場合があります。
クライアントは、再開に必要な状態をセッションチケットとともに保存できます。サーバーは、セッションチケットを使用して状態を保持できます。
セッション再開により、サーバーは元の接続のアクティビティと再開された接続を関連付けることができますが、これはクライアントにとってプライバシーの問題となる可能性があります。クライアントは、この関連付けを避けるために、再開を有効にしないことを選択できます。クライアントは、サーバー以外のエンティティが接続を関連付けることができるようになるため、チケットを再利用すべきではありません。TLS13の付録C.4を参照してください。
4.6. 0-RTT
QUICの0-RTT機能を使用すると、クライアントはハンドシェイクが完了する前にアプリケーションデータを送信できます。これは、以前の接続でネゴシエートされたパラメータを再利用することで実現されます。この機能を有効にするには、クライアントが重要なパラメータを記憶し、サーバーが同じ情報を復元できるようにTLSセッションチケットをサーバーに提供する必要があります。
この情報には、TLS13で規定されているTLS状態を決定するパラメータ、QUICトランスポートパラメータ、選択されたアプリケーションプロトコル、およびアプリケーションプロトコルが必要とする可能性のある情報が含まれます(セクション4.6.3を参照)。これらの情報によって、0-RTTパケットとその内容がどのように構成されるかが決まります。
両方のエンドポイントで同じ情報が利用可能であることを保証するため、0-RTTの確立に使用されるすべての情報は、同じ接続から取得されます。エンドポイントは、0-RTTの送信または処理に影響を与える可能性のある情報を選択的に無視することはできません。
TLS13では、最初の接続から0-RTTの使用試行までの時間制限を7日間と定めています。0-RTTの使用には他にも制約があり、特にリプレイ攻撃への潜在的な脆弱性が挙げられます。詳細は9.2項を参照してください。
4.6.1. 0-RTTの有効化
NewSessionTicketメッセージのTLS early_data拡張は、サーバーが受け入れ可能なTLS 0-RTTデータの量を(max_early_data_sizeパラメータで)伝えるために定義されています。QUICはTLSの早期データを使用しません。QUICは早期データを伝送するために0-RTTパケットを使用します。そのため、max_early_data_sizeパラメータは、サーバーがQUIC 0-RTTデータを受け入れる意思があることを示すセンチネル値0xffffffffを保持するように再利用されます。サーバーが0-RTTデータを受け入れないことを示すには、NewSessionTicketからearly_data拡張を省略します。クライアントがQUIC 0-RTTで送信できるデータ量は、サーバーが提供するinitial_max_dataトランスポートパラメータによって制御されます。
サーバーは、max_early_data_sizeフィールドに0xffffffff以外の値を設定したearly_data拡張を送信してはなりません。クライアントは、early_data拡張に0xffffffff以外の値が含まれているNewSessionTicketを受信した場合、PROTOCOL_VIOLATIONタイプの接続エラーとして処理しなければなりません。
0-RTTパケットを送信したいクライアントは、後続のハンドシェイクのClientHelloメッセージでearly_data拡張を使用します(TLS13の4.2.10項を参照)。その後、アプリケーションデータを0-RTTパケットで送信します。
0-RTTを試みるクライアントは、サーバーがNEW_TOKENフレームを送信した場合、アドレス検証トークンを提供することもできます(QUIC-TRANSPORTの8.1項を参照)。
4.6.2. 0-RTTの受け入れと拒否
サーバーは、EncryptedExtensionsにearly_data拡張を送信することで0-RTTを受け入れます(TLS13の4.2.10項を参照)。その後、サーバーは受信した0-RTTパケットを処理し、確認応答を送信します。
サーバーは、early_data拡張を含まないEncryptedExtensionsを送信することで0-RTTを拒否します。TLS HelloRetryRequestを送信する場合、サーバーは常に0-RTTを拒否します。0-RTTを拒否する場合、サーバーは処理可能であっても、0-RTTパケットを処理してはなりません。0-RTTが拒否された場合、クライアントは、0-RTTパケットの確認応答を受信した場合、その状態を検出できるのであれば、PROTOCOL_VIOLATIONタイプの接続エラーとして扱うべきです。
0-RTTが拒否された場合、クライアントが想定していたすべての接続特性が誤っている可能性があります。これには、アプリケーションプロトコルの選択、トランスポートパラメータ、およびアプリケーション構成が含まれます。したがって、クライアントは、ストリームにバインドされているアプリケーション状態を含め、すべてのストリームの状態をリセットする必要があります。
クライアントは、再試行パケットまたはバージョンネゴシエーションパケットを受信した場合、0-RTTを再試行することができます。これらのパケットは、0-RTTの拒否を意味するものではありません。
4.6.3. 0-RTT構成の検証
サーバは、early_data拡張を含むClientHelloを受信すると、クライアントからの0-RTTデータを受け入れるか拒否するかを決定する必要があります。この決定の一部はTLSスタックによって行われます(例えば、再開される暗号スイートがClientHelloに含まれているかどうかの確認など。TLS13の4.2.10項を参照)。TLSスタックが0-RTTデータを拒否する理由がない場合でも、QUICスタックまたはQUICを使用するアプリケーションプロトコルは、再開されたセッションに関連付けられたトランスポートまたはアプリケーションの構成がサーバの現在の構成と互換性がないために、0-RTTデータを拒否する可能性があります。
QUICでは、0-RTTセッションチケットにトランスポート状態を関連付ける必要があります。これを実装する一般的な方法の1つは、ステートレスセッションチケットを使用し、この状態をセッションチケットに格納することです。QUICを使用するアプリケーションプロトコルも、状態の関連付けまたは格納に関して同様の要件を持つ場合があります。この関連状態は、0-RTTデータを拒否するかどうかを判断するために使用されます。例えば、HTTP/3の設定QUIC-HTTPでは、クライアントからの0-RTTデータの解釈方法が規定されています。QUICを使用する他のアプリケーションでは、0-RTTデータの受け入れまたは拒否を判断するための要件が​​異なる場合があります。
4.7. HelloRetryRequest
HelloRetryRequestメッセージ(TLS13の4.1.4項を参照)は、クライアントに鍵共有などの新しい情報の提供を要求したり、クライアントの何らかの特性を検証したりするために使用できます。QUICの観点から見ると、HelloRetryRequestは、初期パケットに含まれる他の暗号化ハンドシェイクメッセージと区別されません。原則として、この機能をアドレス検証に使用することも可能ですが、QUIC実装では代わりにRetry機能を使用するべきです。QUIC-TRANSPORTの8.1項を参照してください。
4.8. TLSエラー
TLSでエラーが発生した場合、TLS13のセクション6で定義されている適切なアラートが生成されます。
TLSアラートはQUIC接続エラーに変換されます。アラート記述値に0x0100を加算することで、CRYPTO_ERROR用に予約されている範囲のQUICエラーコードが生成されます(QUIC-TRANSPORTのセクション20.1を参照)。生成された値は、タイプ0x1cのQUIC CONNECTION_CLOSEフレームで送信されます。
QUICは「致命的」レベルのアラートのみを伝達できます。TLS 1.3では、「警告」レベルの唯一の用途は接続の切断を通知することです(TLS13のセクション6.1を参照)。QUICは接続終了のための代替メカニズムを提供しており、TLS接続はエラーが発生した場合にのみ切断されるため、QUICエンドポイントはTLSからのアラートを「致命的」レベルとして処理する必要があります。
QUICでは、特定のエラーコードの代わりに汎用コードを使用することが認められています。詳細はQUIC-TRANSPORTのセクション11を参照してください。TLSアラートの場合、これはあらゆるアラートをハンドシェイク失敗(QUICでは0x0128)などの汎用アラートに置き換えることを含みます。エンドポイントは、機密情報の漏洩を防ぐために汎用エラーコードを使用する場合があります。
4.9. 未使用キーの破棄
QUICが新しい暗号化レベルへの移行を完了すると、以前の暗号化レベルのパケット保護キーは破棄されます。これはハンドシェイク中、およびキーの更新時に複数回発生します。詳細はセクション6を参照してください。
新しいキーが利用可能になった場合でも、パケット保護キーはすぐに破棄されません。より低い暗号化レベルのパケットにCRYPTOフレームが含まれている場合、そのデータを再送信するフレームは同じ暗号化レベルで送信されなければなりません。同様に、エンドポイントは、確認応答対象のパケットと同じ暗号化レベルのパケットに対して確認応答を生成します。したがって、より新しい暗号化レベルの鍵が利用可能になった後も、短期間はより低い暗号化レベルの鍵が必要になる場合があります。
エンドポイントは、特定の暗号化レベルの鍵を破棄することはできません。ただし、その暗号化レベルのピアからすべての暗号化ハンドシェイクメッセージを受信し、かつピアも同様にハンドシェイクメッセージを受信して​​いる場合は除きます。この確認方法については、初期鍵(4.9.1項)とハンドシェイク鍵(4.9.2項)でそれぞれ異なる方法が提供されています。これらの方法は、ピアが必要なすべての確認応答を受信して​​いない可能性があるため、その暗号化レベルでのパケットの送受信を妨げるものではありません。
エンドポイントは古い鍵を保持することができますが、新しいデータは現在利用可能な最高レベルの暗号化レベルで送信されなければなりません。以前の暗号化レベルで送信されるのは、ACKフレームとCRYPTOフレーム内のデータの再送信のみです。これらのパケットには、PADDINGフレームが含まれる場合もあります。
4.9.1. 初期鍵の破棄
初期シークレット(セクション5.2)で保護されたパケットは認証されないため、攻撃者は接続を妨害する目的でパケットを偽装する可能性があります。このような攻撃を制限するため、初期パケット保護鍵は他の鍵よりも積極的に破棄されます。
ハンドシェイクパケットが正常に使用されると、初期パケットの交換は不要になります。これらの鍵は、初期パケットからすべてのCRYPTOフレームを受信した後でなければ生成できないためです。したがって、クライアントは最初にハンドシェイクパケットを送信した時点で初期鍵を破棄しなければならず、サーバーは最初にハンドシェイクパケットを正常に処理した時点で初期鍵を破棄しなければなりません。エンドポイントは、この時点以降、初期パケットを送信してはなりません。
これにより、初期暗号化レベルの損失回復状態が放棄され、未処理の初期パケットはすべて無視されます。
4.9.2.ハンドシェイクキーの破棄
エンドポイントは、TLSハンドシェイクが確認された時点で、ハンドシェイクキーを破棄しなければなりません(4.1.2項)。
4.9.3. 0-RTTキーの破棄
0-RTTパケットと1-RTTパケットは同じパケット番号空間を共有しており、クライアントは1-RTTパケットを送信した後、0-RTTパケットを送信しません(5.6項)。
したがって、クライアントは1-RTTキーをインストールした時点で、0-RTTキーは不要となるため、直ちに破棄すべきです。
また、サーバーは1-RTTパケットを受信した時点で、0-RTTキーを破棄しても構いません。ただし、パケットの順序変更により、0-RTTパケットが1-RTTパケットの後に届く場合があります。サーバーは、順序変更されたパケットを復号化するために、0-RTTキーを一時的に保持しても構いません。これにより、パケットの内容を1-RTTキーで再送信する必要がなくなります。 1-RTTパケットを受信した後、サーバーは0-RTTキーを短時間で破棄しなければなりません。推奨される時間はプローブタイムアウト(PTO、QUIC-RECOVERYを参照)の3倍です。サーバーは、すべての0-RTTパケットを受信したと判断した場合、0-RTTキーをより早く破棄しても構いません。これは、欠落パケット番号を追跡することで可能です。
5. パケット保護
TCP上のTLSと同様に、QUICはTLSハンドシェイクで生成された鍵を用いてパケットを保護します。鍵はTLSによってネゴシエートされたAEADアルゴリズムAEADを使用します。
QUICパケットの保護レベルは、パケットの種類によって異なります。
バージョンネゴシエーションパケットには暗号化による保護はありません。
リトライパケットは、偶発的な改ざんを防ぎ、有効なリトライパケットを生成できるエンティティを制限するために、AEAD_AES_128_GCMを使用します(セクション5.8を参照)。
初期パケットは、クライアントから送信された最初の初期パケットの宛先接続IDフィールドから生成された鍵を用いて、AEAD_AES_128_GCMを使用します(セクション5.2を参照)。
その他のすべてのパケットは、TLSによってネゴシエートされた鍵とアルゴリズムを用いて、機密性と完全性を確保するための強力な暗号化による保護を備えています。
このセクションでは、ハンドシェイクパケット、0-RTTパケット、および1-RTTパケットへのパケット保護の適用方法について説明します。初期パケットにも同様のパケット保護プロセスが適用されます。しかし、初期パケットに使用される鍵は容易に特定できるため、これらのパケットは機密性や完全性の保護を受けていないとみなされます。再送パケットは固定鍵を使用するため、同様に機密性や完全性の保護を受けていません。
5.1. パケット保護鍵
QUICは、TLSがレコード保護鍵を生成するのと同様の方法でパケット保護鍵を生成します。
各暗号化レベルには、双方向のパケットを保護するための個別の秘密値があります。これらのトラフィック秘密値はTLSによって生成され(TLS13の7.1項を参照)、初期暗号化レベルを除くすべての暗号化レベルでQUICによって使用されます。初期暗号化レベルの秘密値は、5.2項で説明するように、クライアントの初期宛先接続IDに基づいて計算されます。
パケット保護に使用される鍵は、TLSが提供するKDFを使用してTLS秘密値から計算されます。 TLS 1.3では、TLS13の7.1節で説明されているHKDF-Expand-Label関数が、ネゴシエートされた暗号スイートのハッシュ関数とともに使用されます。QUICにおけるHKDF-Expand-Labelの使用はすべて、長さゼロのコンテキストを使用します。
文字列で記述されるラベルは、引用符や末尾のNULLバイトなしでASCII ASCIIを使用してバイトとしてエンコードされることに注意してください。
他のバージョンのTLSは、QUICで使用するために同様の関数を提供する必要があります。
現在の暗号化レベルの秘密鍵とラベル「quic key」は、AEAD鍵を生成するためにKDFに入力されます。ラベル「quic iv」は、初期化ベクトル(IV)の導出に使用されます(5.3節参照)。ヘッダー保護鍵には「quic hp」ラベルが使用されます(5.4節参照)。これらのラベルを使用することで、QUICとTLS間の鍵分離が実現されます(9.6節参照)。
鍵生成には「quic key」と「quic hp」の両方が使用されるため、これらのラベルとともにHKDF-Expand-Labelに渡される長さは、AEADまたはヘッダー保護アルゴリズムにおける鍵のサイズによって決まります。「quic iv」に渡される長さは、AEAD nonceの最小長、またはそれより大きい場合は8バイトです。詳細はAEADを参照してください。
初期シークレットに使用されるKDFは、常にTLS 1.3のHKDF-Expand-Label関数です。詳細はセクション5.2を参照してください。
5.2. 初期シークレット
初期パケットはパケット保護プロセスを適用しますが、クライアントの最初の初期パケットの宛先接続IDフィールドから導出されたシークレットを使用します。
このシークレットは、HKDF-Extract(HKDFの2.2項を参照)を用いて、ソルト0x38762cf7f55934b34d179ae6a4c80cadccbb7f0aと宛先接続IDフィールドの入力キーイングマテリアル(IKM)を使用して決定されます。これにより、送信用と受信用の2つのシークレットを導出するために使用される中間擬似乱数キー(PRK)が生成されます。
クライアントが初期パケットを構築するために使用するシークレットは、PRKとラベル「client in」をTLS TLS13のHKDF-Expand-Label関数への入力として使用し、32バイトのシークレットを生成します。サーバーが構築するパケットは、ラベル「server in」を使用して同じプロセスで構築されます。 HKDFが初期秘密鍵とキーを生成する際に使用するハッシュ関数はSHA-256 SHAです。
このプロセスを擬似コードで表すと以下のようになります。
code:pseudocode
initial_salt = 0x38762cf7f55934b34d179ae6a4c80cadccbb7f0a
initial_secret = HKDF-Extract(initial_salt,
client_dst_connection_id)
client_initial_secret = HKDF-Expand-Label(initial_secret,
"client in", "",
Hash.length)
server_initial_secret = HKDF-Expand-Label(initial_secret,
"server in", "",
Hash.length)