当記事はTLS 1.3に関連した記述のまとめです。
HandShake プロトコル
HandshakeプロトコルはTLSにおいて暗号化通信を開始する以前において双方の暗号化通信に必要なセキュリティ的なネゴシエーションを行うために使用されます。
Handshakeプロトコルは相互接続性、パフォーマンス、セキュリティの各要件を最大公約数的に満たすことを意図しています。
Handshakeプロトコルの目的は下記の4点です。
1.接続で使用するパラメーターを提示し、共通のセキュリティパラメーターを双方が合意する
2.サーバーの真正性(と必要な場合はクライアントの真正性も)を検証する
3.暗号鍵を(複数)生成する
4.ハンドシェイクメッセージがネットワーク攻撃者によって書き換えられていないことを検証する
HandshakeプロトコルはRecordプロトコルに格納されて送受信されます。また、送信側で複数のメッセージに分割(フラグメンテーション)されて送信する場合もあります。
Handshakeプロトコルは多様な種類のメッセージを格納できるように簡潔なラッパーとして定義されています。
先頭の1バイト目がメッセージタイプ、2~4バイト目がメッセージ長、5バイト目以降がメッセージの本体です。
※原文ではヘッダー、という表現はありませんが、ヘッダーなんだろうな、という事で記載しています。
Handshakeの具体的な構文例はこちら
struct {
HandshakeType msg_type;
uint24 length;
HandshakeMessage message;
} Handshake;
HandshakeMessageのメッセージタイプは様々ありますが、送受信可能なメッセージは通信の状態に応じて定義されていて変化します。この定義に反したメッセージタイプを受信した場合は接続が即時に終了されます。Handshakeプロトコルでの主要なメッセージとその拡張を示した図が下記です。

鍵交換
TLS 1.3のハンドシェイクはTLS 1.2 以前とは完全に別物として設計されています。TLS 1.2以前のプトロコルで長い間に後付けされてきた機能を整理しシンプルになっているものの、将来的なセキュリティ拡張への対応とネットワーク上での互換性維持のトレードオフもありハンドシェイク自体はTLS 1.2以前よりも複雑になっています。
クライアントからの通信開始
TLS 1.2以前は最初にクライアントから通信を送り、サーバー側で鍵交換の種類を選択して応答していました。TLS 1.3ではクライアントから1つまたは複数の鍵交換の方法を提案し(必要なパラメーターも同時にサーバーに送信して)通信を開始します。サーバーはクライアントから受け取った条件を基に通信をかいしできるので上記のTLS 1.2の初回の通信部分を省略でき、結果RTT(Round Trip Time)を半分に短縮できます。
0-RTT
TLS 1.3では以前にサーバーとやり取りしたことのあるクライアントはClientHelloメッセージの直後に直ちにアプリケーションデータを暗号化して送信できます。 これにより通信のパフォーマンスは向上しますが、セキュリティ面ではマイナスでもあります。
サーバー拡張の暗号化
サーバーはクライアントから提示された接続のためのパラメーターに合意できればServerHelloメッセージで応答します。このメッセージ内でクライアントから提示されたうちどれを選択したかを伝え、鍵交換に必要な残り半分の情報を渡します。
TLS 1.2 ではServerHelloメッセージにサーバー拡張もすべて含まれており(つまり暗号化されていない状態で送信されており)、攻撃者に情報を見られる可能性がありました。TLS 1.3では必要なサーバー拡張だけを含みます。残りのサーバー拡張は新しく規定された EncryptedExtensionsメッセージを使って暗号化されて送信されます。
ServerHelloでネゴシエーションした後の通信はすべて暗号化されます。
ハンドシェイク移行で使うメッセージ
なるべく多くの通信を暗号化しようという意図からTLS 1.3で追加された?メッセージとしてsession resumption やクライアント認証のためのメッセージがあります。
暗号に関するネゴシエーション
毎回のハンドシェイクで実行する内容は以下の3つです。
・暗号化アルゴリズムとハッシュ関数の選択
・鍵交換の実行
・通信相手とハンドシェイク自体のメッセージ認証
・暗号化アルゴリズム
クライアントは自身が対応している暗号化の一覧をClientHelloメッセージで指定します。サーバーはその一覧から通信で使用する暗号化を1つ選択します。
鍵交換と必要な情報の共有
TLS 1.3では上記のClientHelloメッセージのkey_share拡張にクライアントが使用可能なうちの主要な1つまたは2つの鍵交換アルゴリズムで必要なすべての情報を提供しています。この結果、サーバーはアルゴリズムを選択して即座に鍵交換を完遂できます。パフォーマンス面でTLS 1.3がTLS 1.2以前より有利な点です。
鍵交換で対応しているグループ
クライアントは自身がサーバーに最初に提案した1つまたは2つの鍵交換アルゴリズムの選択肢で合意できない場合に備えて、別な鍵交換アルゴリズムのグループ?もsupported_groups拡張で伝えます。これは鍵交換がDHEの場合でもECDHEの場合でも共通です。
署名アルゴリズム
ハンドシェイク中にサーバーが利用できるアルゴリズムを伝える際には signature_algorithms拡張を使用します。デフォルトではsignature_algorithms拡張で決定したアルゴリズムをTLSプロトコルの署名と決定し、証明書を用いた署名の検証にも使用します。
ただし、signature_algorithms拡張を使って証明書の署名に別なアルゴリズムを指定することもできます。
事前共有鍵
TLS 1.3では認証に(署名に利用する)証明書もしくは事前共有鍵(pre-shared keys) を利用できます。事前共有鍵は特に、session resumptionで有効です(後述)。
クライアントが1つまたは複数の事前共有鍵を持つ場合、psk_key_exchange_modes拡張でサーバーに通知できます。また事前共有鍵に識別子IDを指定する場合はpre_shard_key拡張を使います。
