2
0

More than 1 year has passed since last update.

QUICを組み込み向け TLS で動かしてみる

Last updated at Posted at 2022-09-29

概要

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のスタック構造を簡単に示しています。

image.png
図 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を行っていることが分かります。
image.png
パケットの一部を次の図のように拡大してます。UDP上に QUIC パケットが乗り、TLSの ClientHello メッセージを送信していることが分かります。ClientHello のメッセージそのものは数百バイトですが、QUIC仕様にしたがって 1200 bytes になるようにパディングされています。
image.png

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 を設定します。

image.png

次に[編集]->[設定]->[Protocols]->[TLS] (Pre)-Master-Secret log filename を設定します。
image.png

設定が終わったら再度 wsslclient プログラムを実行します。

$ ./examples/wsslclient nghttp2.org 4433

ServerHello 以降のメッセージが復号されています。
image.png
メッセージの内容を拡大表示します。ALPN TLS 拡張子に HTTP3 プロトコルで通信行うことをサーバーが選択し、サーバーからの QUIC パラメータが QUIC Transport Parameters TLS 拡張子に収められていることが分かります。またバイト数の大きな証明書(Certificate)は複数のQUICパケットに分割され送信されています。

image.png

まとめ

今回は、次世代のインターネット通信規格として注目をあつめる 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/

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0