概要
IETFが2022年6月6日にWeb通信における新しい通信プロトコル HTTP/3 をRFC9114として発行しました。最大の特徴は、トランスポートのプロトコルに QUIC(Quick UDP Internet Connections) を採用した点です。QUICはRFC9000 として IETF より 2021 年に発行されている UDP に基づくプロトコルです。QUICはそのプロトコルスタック内で、認証・秘匿性・改ざん検知にTLS 1.3 のハンドシェイクプロトコルを利用しています。
ngtcp2 は、QUIC実装を実現しているオープンソースプロジェクトです。組み込み向け TLS ライブラリの wolfSSL は ngtcp2 への統合をサポートしました。ngtcp2 は wolfSSL を通してQUIC通信を行うことができます。
今回は、ngtcp2 の TLSハンドシェークプロトコルのバックエンドに wolfSSL を利用し HTTP3 の通信をHTTP3テストサーバーに対して行います。また、Wiresharkを使ってそのときの QUIC パケットを覗いてみます。 TLS 1.3 のハンドシェークは ServerHello
以降が暗号化されますが、復号化しその中身を覗いてみることにします。HTTP3 の通信には、nghttp3 を利用します。下記図は今回使用するモジュールとQUIC/HTTP3のスタック構造を簡単に示しています。
ダウンロード、ビルド、インストール
1. wolfSSL のソースコード取得とビルド
最新コードを github から取得します。
$git clone --depth 1 https://github.com/wolfSSL/wolfssl
コンフィグレーションに "--enable-quic"オプション及び必要な機能を指定し QUIC 機能を wolfSSL に組み込みシステムへインストールします。また、のちほど WireShark でパケット復号のために、HAVE_SECRET_CALLBACK 定義も指定しておきます。このオプションは技術評価などのためのものです。運用システムには組み込まないように注意してください。
$ ./autogen.sh
$ ./configure --enable-quic --enable-session-ticket --enable-earlydata --enable-psk CFLAGS="-DHAVE_SECRET_CALLBACK"
$ make && make check
$ sudo make install
2. nghttp3 のソースコード取得とビルド
ngtcp2 の上物として nghttp3 を取得・ビルドします。
2.1 nghttp3 のソースコード取得
github から最新版を clone します。
git clone https://github.com/ngtcp2/nghttp3
2.2 nghttp3 のビルド
以下のコマンドを使って nghttp3 をビルドします。
$ cd nghttp3
$ autoreconf -i
$ ./configure --prefix=$PWD/build --enable-lib-only
$ make && make install
3. ngtcp2 のソースコード取得とビルド
3.1 ngtcp2 のソースコード取得
github から最新版を clone します。
git clone https://github.com/ngtcp2/ngtcp2
3.2 ngtcp2 のビルド
以下のコマンドを使って nghttp2 をビルドします。
$ cd ngtcp2
$ autoreconf -i
$ ./configure PKG_CONFIG_PATH=$PWD/../nghttp3/build/lib/pkgconfig --with-wolfssl --with-libnghttp3
$ make
コンフィグレーション実行時に、Examplesが yes
となっていることを確認します。
....
wolfSSL: yes (CFLAGS='-I/usr/local/include' LIBS='-L/usr/local/lib -lwolfssl')
Examples: yes
no
と表示されている場合、libev
もしくはC++20 対応 g++
がインストールされていない可能性があります。これらをインストールします。
サンプルを動かしてみる
サンプルクライアント・サーバープログラムは、./example/wsslclient
及び wsslserver
となります。クライアントプログラムをテストサーバーに対して接続します。以下のような出力が観測されればビルドは成功しています。
$ ./examples/wsslclient nghttp2.org 4433
I00000000 0x4251297518ee500bc5917beeecb21cbd82 pkt tx pkn=0 dcid=0x37b3baee728d6ba376ffa62ccbe2d23b65de scid=0x4251297518ee500bc5917beeecb21cbd82 version=0x00000001 type=Initial len=0
I00000000 0x4251297518ee500bc5917beeecb21cbd82 frm tx 0 Initial CRYPTO(0x06) offset=0 len=292
I00000000 0x4251297518ee500bc5917beeecb21cbd82 frm tx 0 Initial PADDING(0x00) len=840
I00000000 0x4251297518ee500bc5917beeecb21cbd82 rcv loss_detection_timer=79637890688438 timeout=999
Sent packet: local=[192.168.11.35]:46111 remote=[139.162.123.134]:4433 ecn=0x2 1200 bytes
Timer has already expired: 0.000854689s
....
I00007440 0x4251297518ee500bc5917beeecb21cbd82 rcv target_cwnd=41962 max_delivery_rate_sec=119941 min_rtt=20972262
I00007440 0x4251297518ee500bc5917beeecb21cbd82 rcv loss detection timer canceled
I00007440 0x4251297518ee500bc5917beeecb21cbd82 pkt read packet 43 left 0
Set timer=0.040819s
Set timer=0.040657s
Set timer=29.957907s
ngtcp2_conn_handle_expiry: ERR_IDLE_CLOSE
Wireshark で QUIC パケットを覗いてみる
無事、テストサーバーに接続できたところで、そのパケットを Wireshark で覗いてみます。Wiresharkを起動し、再度、wsslclient
を実行します。パケットのフィルターには下記をしています。
quic && ip.addr == 139.162.123.134 && udp.port == 4433
最初にHandshakeを行っていることが分かります。
パケットの一部を次の図のように拡大してます。UDP上に QUIC パケットが乗り、TLSの ClientHello
メッセージを送信していることが分かります。ClientHello
のメッセージそのものは数百バイトですが、QUIC仕様にしたがって 1200 bytes になるようにパディングされています。
QUICは、TLS 1.3 のハンドシェークプロトコルを利用していることから ServerHello
パケット以降の内容は暗号化されており、詳細を見ることが出来ません。詳細をみるために復号するには SSLKEYLOGFILE 環境変数を以下のように設定します。
export SSLKEYLOGFILE=/path/to/quic_keylog_file
注意点として、Wireshark は QUIC プロトコルを早くからサポートしていますが Wireshark のバージョンによっては QUIC プロトコルのDraftバージョンのみをサポートし、それより新しいプロトコルバージョンは期待した通り復号できない場合があります。この記事を書いている段階(2022.0923)で Ubuntu 上の Wireshark version 3.6.5 で QUIC 正式バージョンを復号することが出来ました。
Wireshark を起動します。[編集]->[設定]->[Protocols]->[QUIC]のポートに4433 を設定します。
次に[編集]->[設定]->[Protocols]->[TLS] (Pre)-Master-Secret log filename を設定します。
設定が終わったら再度 wsslclient
プログラムを実行します。
$ ./examples/wsslclient nghttp2.org 4433
ServerHello
以降のメッセージが復号されています。
メッセージの内容を拡大表示します。ALPN TLS 拡張子に HTTP3 プロトコルで通信行うことをサーバーが選択し、サーバーからの QUIC パラメータが QUIC Transport Parameters TLS 拡張子に収められていることが分かります。またバイト数の大きな証明書(Certificate)は複数のQUICパケットに分割され送信されています。
まとめ
今回は、次世代のインターネット通信規格として注目をあつめる QUIC のTLSハンドシェークプロトコルのバックエンドを wolfSSL で動かしてみました。また、QUIC実装の一部を垣間見るためにTLSハンドシェークプロトコルを復号してみました。
QUICは、今後、組み込む向けにも軽量・高速な通信規格としても注目されるものと思われます。
参照リンク:
ngtcp2 : https://github.com/ngtcp2/ngtcp2
nghttp3 : https://github.com/ngtcp2/nghttp3
wolfBlog : https://wolfssl.jp/wolfblog/2022/08/17/wolfssl-quic-support/
Curl 作者 Daniel Stenberg のブログ : https://daniel.haxx.se/blog/2022/08/15/quic-and-http-3-with-wolfssl/