メインコンテンツまでスキップ

7. Cryptographic Computations (暗号計算)

TLSハンドシェイクは、以下に詳述するように、実際の動作鍵材料を作成するために組み合わせられる1つ以上の入力秘密を確立します。鍵導出プロセスには、入力秘密とハンドシェイクトランスクリプトの両方が組み込まれています。ハンドシェイクトランスクリプトにはHelloメッセージのランダム値が含まれているため、同じPSKが複数の接続に使用される場合のように、同じ入力秘密が使用される場合でも、任意のハンドシェイクは異なるトラフィック秘密を持つことに注意してください。

7.1. Key Schedule (鍵スケジュール)

鍵導出プロセスは、HKDF [RFC5869]で定義されているHKDF-ExtractおよびHKDF-Expand関数、および以下で定義されている関数を使用します:

HKDF-Expand-Label(Secret, Label, Context, Length) =
HKDF-Expand(Secret, HkdfLabel, Length)

Where HkdfLabel is specified as:

struct {
uint16 length = Length;
opaque label<7..255> = "tls13 " + Label;
opaque context<0..255> = Context;
} HkdfLabel;

Derive-Secret(Secret, Label, Messages) =
HKDF-Expand-Label(Secret, Label,
Transcript-Hash(Messages), Hash.length)

Transcript-HashおよびHKDFで使用されるHash関数は、暗号スイートのハッシュアルゴリズムです。Hash.lengthは、その出力長(バイト単位)です。Messagesは、ハンドシェイクメッセージタイプと長さフィールドを含むが、レコード層ヘッダーを含まない、指定されたハンドシェイクメッセージの連結です。場合によっては、長さゼロのContext(""で示される)がHKDF-Expand-Labelに渡されることに注意してください。このドキュメントで指定されているラベルはすべてASCII文字列であり、末尾のNULバイトは含まれていません。

注: 一般的なハッシュ関数では、12文字より長いラベルを計算するには、ハッシュ関数の追加の反復が必要です。この仕様のラベルは、すべてこの制限内に収まるように選択されています。

鍵は、HKDF-ExtractおよびDerive-Secret関数を使用して2つの入力秘密から導出されます。新しい秘密を追加するための一般的なパターンは、Saltを現在の秘密状態とし、Input Keying Material(IKM)を追加される新しい秘密として、HKDF-Extractを使用することです。このバージョンのTLS 1.3では、2つの入力秘密は次のとおりです:

  • PSK(外部で確立されるか、以前の接続のresumption_master_secret値から導出される事前共有鍵)
  • (EC)DHE共有秘密(セクション7.4)

これにより、以下の図に示す完全な鍵導出スケジュールが生成されます。この図では、次の書式設定規則が適用されます:

  • HKDF-Extractは、上からSalt引数を取り、左からIKM引数を取り、その出力を下に、出力の名前を右に描画されます。
  • Derive-SecretのSecret引数は、入力矢印で示されます。たとえば、Early Secretは、client_early_traffic_secretを生成するためのSecretです。
  • "0"は、ゼロに設定されたHash.lengthバイトの文字列を示します。
          0
|
v
PSK -> HKDF-Extract = Early Secret
|
+-----> Derive-Secret(., "ext binder" | "res binder", "")
| = binder_key
|
+-----> Derive-Secret(., "c e traffic", ClientHello)
| = client_early_traffic_secret
|
+-----> Derive-Secret(., "e exp master", ClientHello)
| = early_exporter_master_secret
v
Derive-Secret(., "derived", "")
|
v
(EC)DHE -> HKDF-Extract = Handshake Secret
|
+-----> Derive-Secret(., "c hs traffic",
| ClientHello...ServerHello)
| = client_handshake_traffic_secret
|
+-----> Derive-Secret(., "s hs traffic",
| ClientHello...ServerHello)
| = server_handshake_traffic_secret
v
Derive-Secret(., "derived", "")
|
v
0 -> HKDF-Extract = Master Secret
|
+-----> Derive-Secret(., "c ap traffic",
| ClientHello...server Finished)
| = client_application_traffic_secret_0
|
+-----> Derive-Secret(., "s ap traffic",
| ClientHello...server Finished)
| = server_application_traffic_secret_0
|
+-----> Derive-Secret(., "exp master",
| ClientHello...server Finished)
| = exporter_master_secret
|
+-----> Derive-Secret(., "res master",
ClientHello...client Finished)
= resumption_master_secret

ここでの一般的なパターンは、図の左側に示されている秘密は、コンテキストのない生のエントロピーであるのに対し、右側の秘密には、ハンドシェイクコンテキストが含まれているため、追加のコンテキストなしで動作鍵を導出するために使用できることです。同じ秘密であっても、Derive-Secretへの異なる呼び出しが異なるMessages引数を取る場合があることに注意してください。0-RTT交換では、Derive-Secretは4つの異なるトランスクリプトで呼び出されます。1-RTTのみの交換では、3つの異なるトランスクリプトで呼び出されます。

特定の秘密が利用できない場合、ゼロに設定されたHash.lengthバイトの文字列で構成される0値が使用されます。これは、ラウンドをスキップすることを意味するものではないため、PSKが使用されていない場合でも、Early Secretは依然としてHKDF-Extract(0, 0)になります。binder_keyの計算では、ラベルは外部PSK(TLS外でプロビジョニングされたもの)の場合は"ext binder"、再開PSK(以前のハンドシェイクの再開マスター秘密としてプロビジョニングされたもの)の場合は"res binder"です。異なるラベルにより、あるタイプのPSKを別のタイプに置き換えることが防止されます。

サーバーが最終的に選択するPSKに応じて、複数の潜在的なEarly Secret値があります。クライアントは、各潜在的なPSKに対して1つを計算する必要があります。PSKが選択されない場合、ゼロPSKに対応するEarly Secretを計算する必要があります。

特定の秘密から導出されるすべての値が計算されたら、その秘密は消去されるべきです(SHOULD)。

7.2. Updating Traffic Secrets (トラフィック秘密の更新)

ハンドシェイクが完了すると、セクション4.6.3で定義されているKeyUpdateハンドシェイクメッセージを使用して、どちら側も送信トラフィック鍵を更新できます。次世代のトラフィック鍵は、このセクションで説明されているようにclient_/server_application_traffic_secret_Nからclient_/server_application_traffic_secret_N+1を生成し、セクション7.3で説明されているようにトラフィック鍵を再導出することによって計算されます。

次世代のapplication_traffic_secretは次のように計算されます:

application_traffic_secret_N+1 =
HKDF-Expand-Label(application_traffic_secret_N,
"traffic upd", "", Hash.length)

client_/server_application_traffic_secret_N+1とそれに関連するトラフィック鍵が計算されたら、実装はclient_/server_application_traffic_secret_Nとそれに関連するトラフィック鍵を削除すべきです(SHOULD)。

7.3. Traffic Key Calculation (トラフィック鍵計算)

トラフィック鍵材料は、次の入力値から生成されます:

  • 秘密値
  • 生成される特定の値を示す目的値
  • 生成される鍵の長さ

トラフィック鍵材料は、次を使用して入力トラフィック秘密値から生成されます:

[sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length)
[sender]_write_iv = HKDF-Expand-Label(Secret, "iv", "", iv_length)

[sender]は送信側を示します。以下の表は、各レコードタイプのSecret値を示しています。

Record TypeSecret
0-RTT Applicationclient_early_traffic_secret
Handshake[sender]_handshake_traffic_secret
Application Data[sender]_application_traffic_secret_N

すべてのトラフィック鍵材料は、基礎となるSecretが変更されるたびに(たとえば、ハンドシェイクからアプリケーションデータ鍵に変更する場合、または鍵更新時に)再計算されます。

7.4. (EC)DHE Shared Secret Calculation ((EC)DHE共有秘密計算)

7.4.1. Finite Field Diffie-Hellman (有限体Diffie-Hellman)

有限体グループの場合、従来のDiffie-Hellman [DH76]計算が実行されます。交渉された鍵(Z)は、ビッグエンディアン形式でエンコードし、素数のサイズまでゼロで左パディングすることにより、バイト文字列に変換されます。このバイト文字列は、上記で指定されているように、鍵スケジュールで共有秘密として使用されます。

この構造は、先行ゼロを削除した以前のバージョンのTLSとは異なることに注意してください。

7.4.2. Elliptic Curve Diffie-Hellman (楕円曲線Diffie-Hellman)

secp256r1、secp384r1、およびsecp521r1の場合、ECDH計算(パラメータと鍵生成、および共有秘密計算を含む)は、鍵導出関数(KDF)として恒等写像を持つECKAS-DH1スキームを使用して[IEEE1363]に従って実行されるため、共有秘密はオクテット文字列として表されるECDH共有秘密楕円曲線点のx座標です。FE2OSP(Field Element to Octet String Conversion Primitive)によって出力されるこのオクテット文字列(IEEE 1363用語ではZ)は、任意の与えられたフィールドに対して一定の長さを持つことに注意してください。このオクテット文字列で見つかった先行ゼロは切り捨ててはなりません(MUST NOT)。

(この恒等KDFの使用は技術的なものであることに注意してください。完全な図は、TLSが他の秘密を計算する以外にこの秘密を直接使用しないため、ECDHが自明ではないKDFで採用されているということです。)

X25519とX448の場合、ECDH計算は次のとおりです:

  • KeyShareEntry.key_exchange構造に入れる公開鍵は、適切な長さの秘密鍵(スカラー入力に)と標準の公開ベースポイント(u座標ポイント入力に)にECDHスカラー乗算関数を適用した結果です。

  • ECDH共有秘密は、秘密鍵(スカラー入力に)とピアの公開鍵(u座標ポイント入力に)にECDHスカラー乗算関数を適用した結果です。出力は、処理なしで生のまま使用されます。

これらの曲線の場合、実装は、Diffie-Hellman共有秘密を計算するために[RFC7748]で指定されているアプローチを使用すべきです(SHOULD)。実装は、計算されたDiffie-Hellman共有秘密がすべてゼロの値であるかどうかを確認し、そうである場合は[RFC7748]のセクション6で説明されているように中止しなければなりません(MUST)。実装者がこれらの楕円曲線の代替実装を使用する場合、[RFC7748]のセクション7で指定されている追加のチェックを実行すべきです(SHOULD)。

7.5. Exporters (エクスポーター)

[RFC5705]は、TLS疑似ランダム関数(PRF)の観点からTLSの鍵材料エクスポーターを定義しています。このドキュメントは、PRFをHKDFに置き換えるため、新しい構造が必要です。エクスポーターインターフェースは同じままです。

エクスポーター値は次のように計算されます:

TLS-Exporter(label, context_value, key_length) =
HKDF-Expand-Label(Derive-Secret(Secret, label, ""),
"exporter", Hash(context_value), key_length)

ここで、Secretはearly_exporter_master_secretまたはexporter_master_secretのいずれかです。実装は、アプリケーションによって明示的に指定されない限り、exporter_master_secretを使用しなければなりません(MUST)。early_exporter_master_secretは、0-RTTデータにエクスポーターが必要な設定での使用のために定義されています。早期エクスポーター用の個別のインターフェースが推奨されます(RECOMMENDED)。これにより、エクスポーターユーザーが通常のエクスポーターが必要なときに誤って早期エクスポーターを使用したり、その逆を行ったりすることを回避できます。

コンテキストが提供されない場合、context_valueは長さゼロです。したがって、コンテキストを提供しないことは、空のコンテキストを提供することと同じ値を計算します。これは、空のコンテキストが存在しないコンテキストとは異なる出力を生成したTLSの以前のバージョンからの変更です。このドキュメントの発行時点では、割り当てられたエクスポーターラベルは、コンテキストありとなしの両方で使用されていません。将来の仕様は、同じラベルで空のコンテキストとコンテキストなしの両方を許可するエクスポーターの使用を定義してはなりません(MUST NOT)。エクスポーターの新しい使用法は、値が空である可能性がありますが、すべてのエクスポーター計算でコンテキストを提供すべきです(SHOULD)。

エクスポーターラベルの形式の要件は、[RFC5705]のセクション4で定義されています。