今更ながら、TLS1.3で大幅変更されたハンドシェイクシーケンスを整理する

## はじめに

2018/8に[RFC8446](https://tools.ietf.org/html/rfc8446)として正式公開されたTLS1.3について、2年ほど前に仕事で触れた際は「なんかcipher suiteが新しくなってるけど、ブラウザの設定変更で普通に繋がるからまあいいか～」とか思っていました。
ただ、SSL/TLSのシーケンスをきちんと理解する為に記事を眺めていると[SSL/TLS（SSL3.0～TLS1.2）のハンドシェイクを復習する](https://qiita.com/n-i-e/items/41673fd16d7bd1189a29)に「TLS1.3は全く別物」の文字。

マジか、シーケンスくらいはどこかにメモっときたいな。というわけで記事にしました。
一通り記事を書きながらググっていると、いい記事が沢山見つかったので参考欄に合わせて載せておきます。

## TLS1.2のシーケンス

[RFC5246](https://tools.ietf.org/html/rfc5246)よりTLS1.2のシーケンスを抜粋。

ざっくりいうと以下のように**4回やり取りを行った後に暗号化開始**となります。
1. クライアントがClientHelloでversionやcipher suiteの候補等を送信。
2. サーバーがServerHelloで確定させてから鍵交換。必要なやり取りを行います。
3. クライアントがChangeCipherSpecで終わりを通知
4. サーバーがChangeCipherSpecで終わりを通知
⇒通信の暗号化開始

```
          Client                                               Server
      ClientHello                  -------->
                                                      ServerHello
                                                     Certificate*
                                               ServerKeyExchange*
                                              CertificateRequest*
                                   <--------      ServerHelloDone
      Certificate*
      ClientKeyExchange
      CertificateVerify*
      [ChangeCipherSpec]
      Finished                     -------->
                                               [ChangeCipherSpec]
                                   <--------             Finished
      Application Data             <------->     Application Data
```


詳細は[SSL/TLS（SSL3.0～TLS1.2）のハンドシェイクを復習する](https://qiita.com/n-i-e/items/41673fd16d7bd1189a29)を参照ください。

## TLS 1.3のシーケンス

### 接続シーケンス ⇒ Update!!

[RFC8446](https://tools.ietf.org/html/rfc8446) 2.  Protocol Overviewより抜粋。**暗号化までのやり取りが2に減っています。**
1. クライアントがClientHelloでversionやcipher suiteの候補**とともにkey_shareパラメーターを利用して最初から鍵交換を行う**。
2. サーバーがServerHelloで確定させつつ必要な情報をまとめて送信。
⇒クライアント側で証明書を検証した上で、通信の暗号化開始

まずは**やり取りを減らして高速化をはかる**って感じですかね。

```
          Client                                               Server
Key  ^ ClientHello
Exch | + key_share*
     | + signature_algorithms*
     | + psk_key_exchange_modes*
     v + pre_shared_key*         -------->
                                                       ServerHello  ^ Key
                                                      + key_share*  | Exch
                                                 + pre_shared_key*  v
                                             {EncryptedExtensions}  ^  Server
                                             {CertificateRequest*}  v  Params
                                                    {Certificate*}  ^
                                              {CertificateVerify*}  | Auth
                                                        {Finished}  v
                                 <--------     [Application Data*]
     ^ {Certificate*}
Auth | {CertificateVerify*}
     v {Finished}                -------->
       [Application Data]        <------->      [Application Data]
```

TLS1.3では、鍵交換の方法はPSK/DHE/ECDHE/PSKのいずれかとなっており、どれを使うかをClient Helloのkey_shareパラメーターで選んでいます。
その際DH/ECDHEを使うならkey_shareに共有値を詰めて送信します。(共有鍵は互いのkey_shareが完了後に内部で計算して生成。[DHの仕組みならこんな感じ](https://ja.wikipedia.org/wiki/%E3%83%87%E3%82%A3%E3%83%95%E3%82%A3%E3%83%BC%E3%83%BB%E3%83%98%E3%83%AB%E3%83%9E%E3%83%B3%E9%8D%B5%E5%85%B1%E6%9C%89))
PSKを使う場合はpre_shared_keyに共有鍵を詰めて送信します。

[TLS1.3のハンドシェイクがもう来てる](https://labs.gree.jp/blog/2018/03/16753/)に、TLS1.2と1.3のシーケンスを並べた図があったので合わせて記載しておきます。
明らかにやり取りが減っており、高速化が期待できそうだということが分かります。
![](https://labs.gree.jp/blog/wp-content/uploads/2018/03/1-600x266.png)

### ClientHelloのkey_shareが不正だった場合 ⇒ New!!

TLS1.3では新たにClient Helloで鍵交換にPSK/(EC)DHE/PSK and (EC)DHEのいずれかを使うかを送る仕様が追加されたため、**サーバーが対象の方法をサポートしていないといったエラーケース**が出てきます。
その際は**サーバーから利用可能な鍵交換方法をHelloRetryRequestとともに送り、再度ClientHelloを送りなおしてもらう**仕組みになっています。

[RFC8446](https://tools.ietf.org/html/rfc8446) 2.1.  Incorrect DHE Shareより抜粋。

```
          Client                                               Server
            ClientHello
            + key_share             -------->
                                    <--------         HelloRetryRequest
                                                            + key_share

            ClientHello
            + key_share             -------->
                                                            ServerHello
                                                            + key_share
                                                  {EncryptedExtensions}
                                                  {CertificateRequest*}
                                                         {Certificate*}
                                                   {CertificateVerify*}
                                                             {Finished}
                                    <--------       [Application Data*]
            {Certificate*}
            {CertificateVerify*}
            {Finished}              -------->
            [Application Data]      <------->        [Application Data]
```

### 再接続時のシーケンスは一部省略可能 ⇒ New!!

今までのSSL/TLS通信は再接続時は再度ClientHelloからやり直しが普通でしたが、SSL/TLS 1.3では色々と省略が可能です。
一度PSKでTLS1.3の接続が行われると、サーバーはクライアントへ「PSK identity」情報を渡します。
クライアントは再接続時のClient Hello内pre_shared_keyにidentity情報を詰めて送信することで、再接続であることがわかります。

#### 証明書再交換の省略

[RFC8446](https://tools.ietf.org/html/rfc8446) 2.2.  Resumption and Pre-Shared Key (PSK)より、このケースではCertificate(証明書)のやり取りが省略されています。

```
          ClientHello
          + key_share*
          + pre_shared_key          -------->
                                                          ServerHello
                                                     + pre_shared_key
                                                         + key_share*
                                                {EncryptedExtensions}
                                                           {Finished}
                                    <--------     [Application Data*]
          {Finished}                -------->
          [Application Data]        <------->      [Application Data]
```

#### 0-RTT Data: Client Helloでいきなり暗号化データの送信

再接続の証明が出来るならいきなり暗号化通信してもいいだろ！ということで、**Client Helloに暗号化データも詰めてしまう**なんていうことも可能になります。
というわけで[RFC8446](https://tools.ietf.org/html/rfc8446)2.3.  0-RTT Dataより抜粋。

```
         Client                                               Server
         ClientHello
         + early_data
         + key_share*
         + psk_key_exchange_modes
         + pre_shared_key
         (Application Data*)     -------->
                                                         ServerHello
                                                    + pre_shared_key
                                                        + key_share*
                                               {EncryptedExtensions}
                                                       + early_data*
                                                          {Finished}
                                 <--------       [Application Data*]
         (EndOfEarlyData)
         {Finished}              -------->
         [Application Data]      <------->        [Application Data]
```

#### 情報の使いまわし、セキュリティリスクはないの？⇒ある

上記について、[前方秘匿性](https://ja.wikipedia.org/wiki/Forward_secrecy)が無い為、鍵が漏洩した場合に情報漏洩のリスクがあります。
また、0-RTT Dataはリプレイ攻撃で利用されるリスクもあります。使いどころには気を付けないといけませんね。

[E.5.  Replay Attacks on 0-RTT](https://tools.ietf.org/html/rfc8446#appendix-E.5)で攻撃の説明について、
[8.  0-RTT and Anti-Replay](https://tools.ietf.org/html/rfc8446#section-8)でサーバー側のリプレイ攻撃対策について記載されているそうです。

再接続時のシーケンスについて省略可能なのはRSAの場合のみなので、DHEとECDHEしか使わないというサーバー側設定というのも一つの手かもしれませんね。

## これだけシーケンスが変わって、アプリケーションへの影響はないの？

### Webアプリケーション⇒最新ブラウザが対応してくれている！はず

とりあえずClient HelloメッセージやCipher suiteの考え方等、ハンドシェイクのしょっぱなだけはパラメーターが拡張されているだけでそのままっちゃあそのまま。
証明書もそのまま使用可能。
なので、例えばWebアプリケーションのように今まで全部ブラウザにお任せしていたものであれば、ブラウザがサポートさえしてくれていれば何もしなくてもいいはず。

### その他のクライアント・サーバーアプリケーション⇒各種アップデートが必要

例えばOpenSSLは2018/9/11にリリースされたOpenSSL 1.1.1でTLS1.3に対応。
[OpenSSL 1.1.1 Is Released](https://www.openssl.org/blog/blog/2018/09/11/release111/)

合わせてApacheは2.4.36以降から、nginxなら1.9.14以降からOpenSSL 1.1.1 && TLS 1.3に対応するなど、方々で既に対応がされている模様です。
必要なOSSを最新バージョンにアップデートすれば、TLS1.3を使えるようになると思います。

ただし、ハンドシェイクも一緒なのは最初だけだし、考えが同じCipher suiteだけどTLS 1.3用のものは完全新規だったりするので、OSSの中身が色々と変わっていてもおかしくないですね。

### 運用面⇒メリットやリスクを理解した上で適切な環境構築を

TLS1.3が作られたのは、TLS1.2以前のセキュリティ面や性能面を向上させるためです。
OSSやブラウザが頑張って差分を吸収してくれるのであれば出来るだけ利用したいところ。

一方でシーケンスががらっと変わり、0-RTT Dataのような今までにないケースの登場によるセキュリティリスクも登場します。

実際に運用する際は、そういったセキュリティリスクも考えて適切な環境構築を行う必要がありそうです。

## 参考、関連記事

参考:
[RFC8446 The Transport Layer Security (TLS) Protocol Version 1.3](https://tools.ietf.org/html/rfc8446)
[RFC5246 The Transport Layer Security (TLS) Protocol Version 1.2](https://tools.ietf.org/html/rfc5246)
[SSL/TLS（SSL3.0～TLS1.2）のハンドシェイクを復習する](https://qiita.com/n-i-e/items/41673fd16d7bd1189a29)

TLS1.3関連:
[TLS1.3で使える・使えない暗号アルゴリズム](https://qiita.com/s_machida/items/0f6ef0466503c0f00839)
[TLS1.3は従来のTLS1.2までと何が共通なのか](https://qiita.com/n-i-e/items/ba83c4a1bdfded254d16)
[TLS 1.3 開発日記 その4 フルハンドシェイク](https://kazu-yamamoto.hatenablog.jp/entry/20161208/1481165353)
[TLS1.3のハンドシェイクがもう来てる](https://labs.gree.jp/blog/2018/03/16753/)
[TLS1.3 のプロトコル概要 (RFC8446の2.プロトコル概要部分の日本語訳)](https://www.wolfssl.jp/docs/tls1-3-draft-protocoloverview/)
[TLS 1.3 Overview](https://qiita.com/sylph01/items/3bf7bc2d42da4e0efb37)