マルチパスQUICにおけるOne Way Latencyの考察

QUICの各仕様のdraft-02が出た一方で、マルチパスQUICに関する考察ドラフトが出ています。


マルチパスQUICはマイルストーン上は2017年の後半に拡張の仕様が出て来る予定ですが、それに先立って考察のドラフトが出ている状況です。今後関連するドラフトも出てくるでしょう。


今回提出されているドラフトは、Huaweiの人らが書かれている「One Way Latency Considerations for Multipath in QUIC」というドラフトです。

One Way Latency Considerations for Multipath in QUIC

"One Way Latency"は既にMultiPath TCPでも議論されている概念であり、そちらでもドラフト(URL)が出ています。


そもそも各通信路は、行きと帰りで同じレイテンシとは限りません。そのため複数経路を使用する場合は、RTTではなく片方向のレイテンシ(One Way Latency)を見て送信する経路を選択すべきということになります。RTTの値は輻輳制御やパケロスの検出に利用されますが、同様に複数経路の場合はOne Way Latencyを見るべきです。またレイテンシ同様、輻輳も片方向には輻輳しているが、反対側は輻輳していないというケースがあります。



たとえば、上記図の用に、クライアント・サーバ間で2経路あった場合。RTTだけみれば上の経路のほうが良いですが、クライアントからサーバに送信する場合は下の経路のほうが低レイテンシということもあるわけです。特に動画のキーフレームや重要なシグナル、ロスしたパケットの再送など急ぐものに関しては下の経路で送信したほうが良いということになります。

OWLの測定

このドラフトでは、OWLの測定としてQUICのAckフレームに含まれるタイムスタンプを利用しますが、絶対値を得る方法と、相対値を得る方法について言及しています。


絶対値は各経路の各方向のOWLを計算します。各エンド間で時刻同期している前提になります。NTPといった別のプロトコルを利用して時刻同期を行えば、タイムスタンプを利用してOWLの絶対値が得られます。


相対値は、OWLの絶対値はわかりませんが、各経路の各方向でどの経路のOWLが小さいのかわかれば経路を選択できるようになります。これは時刻同期は必要なく、Ackフレームに含まれるタイムスタンプより計算されます。

TLSにおける証明書チェーンを圧縮する拡張仕様 (RFC 8879)

2020/12/02追記 RFC 8879 とになったので、記事を書き直しました
asnokaze.hatenablog.com


GoogleとCloudflareの方による「Transport Layer Security (TLS) Certificate Compression」という、証明書チェーンの圧縮を行う拡張仕様の提案が出ています。


TLSハンドシェイクの大部分は証明書が占めているらしく、サーバ側から送る証明書チェーンをgzipかbrotliで圧縮できるようにする拡張仕様です。


証明書の圧縮についてはGoogle版QUICでは行っており、TLS1.3で圧縮をサポートするかという議論が昨年末にML上でありました( https://www.ietf.org/mail-archive/web/tls/current/msg22065.html )。


複雑性に見合う効果があるかは分からないが、TLS1.3だけに限る必要はないのではないかという意見が出ていたようです。


IETF版QUICにも証明書の圧縮機能が盛り込まれるかもしれませんが、この拡張を取り込む流れになるのかは分からないです。


QUICはUDPですので、サーバ側からの転送量を削減するのはAMP攻撃対策として大事なのではないかと思います。

TLS Certificate Compression

クライアントは証明書の圧縮に対応している場合、ClientHello内でcompress_server_certificates拡張を使って対応してる圧縮アルゴリズムのリストをサーバに送信します。

       enum {
           zlib(0),
           brotli(1),
           (255)
       } CertificateCompressionAlgorithm;

       struct {
           CertificateCompressionAlgorithm algorithms<1..2^8>;
       } CertificateCompressionAlgorithms;


サーバはクライアントが提示したアルゴリズムに対応していた場合、ServerHelloで同じようにどのアルゴリズムを選択したかを返答します。


ServerHelloで使用するアルゴリズムを通知した場合、Certificateメッセージは以下のように圧縮前の長さと圧縮された証明から構成されます。

       struct {
            uint24 uncompressed_length;
            opaque compressed_certificate_message<1..2^24-1>;
       } Certificate;


server_certificate_type 拡張[RFC7250]の場合も、そのメッセージが圧縮されます。cached_info拡張[RFC7924] とは併用できません。


復号されたあとは、Certificateメッセージは圧縮がなかったようにそのまま処理されます。

CSPの仕様に report-sample が追加された

まだ、WIPではあるもののCSPの仕様に "report-sample" と言う機能が追加されました(URL)。


これは、違反したインラインのScriptやStyleの最初の40文字がレポートに追加されます。外部ファイルの場合はレポートされません。昨年から議論がされていましたが、もともとはFirefoxに以前実装されていたscript-sampleプロパティと同等の機能ですが、Styleも対象になります。


これにより、今までレポートは送られてくるもの攻撃なのかそうじゃないのか分からなかったというケースが、多少なりとも少なくなるのではないだろうか。


すでに、Chromeへの実装が進められています(URL)

report-sample

普通のCSPと同様に、HTTPヘッダもしくはmetaタグで report-sampleを指定します。

Content-Security-Policy: script-src 'nonce-abc' 'report-sample'
<meta http-equiv="Content-Security-Policy" content="style-src 'nonce-abc' 'report-sample'">
sample

下記のようなタグが含まれていた場合

<script>alert(1);</script>


このようにレポートにsampleが追加されます

    {
      ...
      "sample": "alert(1);"
    }

HTTP over マルチキャストQUIC とは

2022/05/16 動きがあったため、記事を新しく書きました
asnokaze.hatenablog.com


Hypertext Transfer Protocol (HTTP) over multicast QUIC」で、マルチキャストのQUIC上でHTTP通信を行う仕様が提案されている。マルチキャストQUICは単方向通信であり、その上でサーバプッシュを行う感じである。



現状QUICの仕様ではIPマルチキャストの利用は想定されていない(もちろん双方向通信)ため、この仕様ではQUICのトランスポートレイヤ、HTTPレイヤを利用しつつも、IPマルチキャストを利用できるように一部の変更(制限)を加えている。


BBC Researchの方が書かれているので、主に動画像配信での利用を考えているんだと思う。


ざっと仕様を読んだのでかいつまんで特徴を紹介する。


また、QUICの仕様はまだ変わる可能性があり、本仕様はそれに併せて変わる旨注意書きがつけられている。

HTTP over マルチキャストQUIC

セッション

マルチキャストQUICでは、単方向通信になる。そのため、コネクションではなくセッションという用語を使用する(コネクションIDではなく、セッションID)。このセッションに参加者が参加する形になる。


セッションのステートは、個々のエンドポイント間で同期されるわけではないが、参加者(送信者も含む)の有無に関して「Idle」、「Half-established」、「Fully-established」、「Finished」の4つの状態が存在する。


Idle状態から開始され、参加者がいなくなるか、明示的に通信を終了することも出来ます。


エンドポイントは他のセッションにマイグレーションすることも出来ます。

セッションの広告

Alt-Svcを用いて、セッションの広告を行います。マルチキャストQUICは単方向通信であるためCryptoハンドシェイクは使用せず、このAlt-SvcにセッションIDやセッションで使用する暗号スイート、暗号化キーが含まれます。受信者は、自身が暗号スイートに対応してるか判断して、セッションに参加するか決めます。また、この時にトランスポートの鍵が提供されます。


プロトコルの識別子は、hqmを使用します。

   Alt-Svc:
       hqm="[ff3e::1234]:2000"; source-address="2001:db8::1"; quic=1;
       session-id=10; session-idle-timeout=60;
       max-concurrent-resources=10; peak-flow-rate=10000;
       cipher-suite=1301; key=4adf1eab9c2a37fd

source-addressは、Source-specific multicast (SSM) に使用される。


また、Alt-Svcはユニキャストの代替サービスも広報出来ます(後述)。

フローコントロール

QUICトランスポートでは、クレジットベースのフロー制御を行っています。しかし、単方向通信では実現不可能であるため、peak-flow-rateというパラメータを使用します。これは、1秒あたりのビット数で表され、Alt-Svcパラメータで通知されます。

送信者はこのレートを超えないようにしなければなりません。しかし、受信者はそれを超えても切断しなくてもよいです。

パケットロスの回復

マルチキャストQUICは単方向通信であるため、ACKフレームは送信者・受信者双方で使用が禁止されます。同様にSTOP_WAITINGフレームも送信できません。


仕様の中では、損失したデータの回復する方法を2つ示しています。

  • Forward Error Correctionを使用する方法。現在IETFの仕様ではFECはオプショナル仕様で、将来仕様策定される予定になっています。
  • ユニキャストで復元する方法。失われたパケットがFECで回復出来ない場合、もしくは閾値を超えて損失した場合は、ユニキャストを用いてデータを取りに行きます。
HTTP2まわり

マルチキャストQUICはHTTP/2のサーバプッシュに依存します。そのため、受信者はサーバプッシュを受け付けられる必要があります。また、マルチキャストQUICは単方向のため、受信者からHEADERSフレームを送ることはありません、そのためサーバからのPUSH_PROMISEフレームの送信は予約されたストリームを参照します


同様に、単方向通信のためにHPACKの動的テーブルを同期するのは不可能です。そのため、動的テーブルは使用できません。


また、単方向通信であるためもちろん受信者からPriorityを指示することも出来ません。

アプリケーションレイヤセキュリティ

HTTPレイヤでコンテンツの完全性と、相手が本当に正しいこと(主に送信者が正しいこと)を確認する事が推奨されます。


RFC3230 Instance Digests in HTTP」を使用し完全性を、「Signing HTTP Messages
」を用いて真正性を担保することを推奨しています。また、再送攻撃については、Dateヘッダを署名対象に含めるように推奨しているが、それ以上については仕様の範囲外としている。


そのほか機密性が必要があれば別途アプリケーションレイヤでの暗号化をするように推奨しているが、鍵の配送については規定していない。

そのほか

Security and Privacy Considerationsとして、セキュリティ(なりすまし、再送攻撃)・プライバシー(広域盗聴など)、DoS攻撃について手厚く書かれています。

たとえば、送信者のなりすましは可能であるため、上記で述べているようなアプリケーションレイヤの対策が推奨されている。

Nginxがstale-while-revalidateに対応した

20170214 追記このコミット(URL)で入った proxy_cache_background_update で、stale-while-revalidateを使わなくてもバックグラウンドでキャッシュ更新するように設定できるようになる模様


このコミット(URL)で、Nginxのproxy_cache機能がstale-while-revalidateとstale-if-errorに対応した。次のNginxのバージョンで使えるようになるだろう。

stale-while-revalidate、stale-if-errorとは

stale-while-revalidateとは、以前「Cache-Controlヘッダのstale-while-revalidateとは」で書いたとおり、Cache-Controlヘッダで指定できる拡張機能である(RFC)。


max-ageでキャッシュが切れたあとに、指定した期間の間それぞれ以下の挙動をする

  • stale-while-revalidate: 一旦キャッシュを使用して、その後にファイルが更新されてないか確認しに行く。まずはキャッシュを使うので待ち時間が少ない
  • stale-if-error: ネットワークエラーがあった場合のみ、キャッシュを使用する


以下のように指定する

Cache-Control: max-age=600, stale-while-revalidate=30

max-age(600sec)後に、30secの間はキャッシュを使用した後にファイルが更新されているか確認し、キャッシュの更新をする。

Nginxの場合

この機能はリバースプロキシ・バックエンドサーバ間でも使用できます。


Nginx側の設定に特殊なことは必要ない。デフォルトでこの機能は有効になっている。

    proxy_cache_path /tmp keys_zone=zone:10m;
    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass http://localhost:8080;
            proxy_cache zone;
...
初回アクセス


初回は普通にリバースプロキシする。バックエンドはHTTPレスポンスにstale-while-revalidateなどを付与する。
Nginxはそのコンテンツをキャッシュする。

stale-while-revalidate、max-age経過後


手元のキャッシュを使用してから、ファイルが更新されてないか確認しに行く。

stale-if-error、max-age経過後


エラーがあった場合のみ手元のキャッシュを使用する。そうでなければいつもどおりに処理する


それぞれ動作確認したところ、上記のように動作していたが。デモが非常にわかりにくいので割愛するorz

WiresharkのTLS1.3対応 動いた

TLS1.3動いたシリーズの第3回目(?)


WiresharkTLSの実装一覧ページ(URL)では、以前よりTLS1.3対応をうたっていたがあまり出来は芳しくなかった。しかし、先日 TLS1.3絡みのコミットが幾つか入ったので、実際に改善されていることを確認した


コミットログ(URL)を見る限りdecryptにも対応しているようなので、復号もできそうだが、TLS1.3になって復号に必要なパラメータが変わったため SSLKEYLOGFILEまわりの仕組みがどうなってるかはよくわからない。picotlsだと、うまくいくかもしれない(URL)

2019年5月追記: すでに復号対応しております。SSLKEYLOGFILEを食わせれば復号できる。

ビルド

今回は簡単に、CUI版であるTsharkをtrustyでビルドする

sudo apt-get install -y flex libglib2.0-dev libpcap-dev bison
git clone https://github.com/wireshark/wireshark.git --depth=1
cd ./wireshark
./autogen.sh
./configure --disable-wireshark  --enable-tshark  --enable-ipv6 --disable-gtktest\
   --disable-glibtest  --disable-editcap --disable-capinfos  --disable-mergecap --disable-reordercap\
   --disable-text2pcap --disable-dftest  --disable-randpkt --disable-airpcap  --disable-rawshark\
   --enable-pcap-ng-default  --without-lua 
make

パケットをパースする

NginxでTLS1.3 動いた(OpenSSL)」で取得したパケットを、Tsharkで読み込む。

ClientHello (一部省略)

vagrant@vagrant:~$ ./wireshark/tshark -r ./tls.pcap   -V
Secure Sockets Layer
    TLSv1 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 512
        Handshake Protocol: Client Hello
            Handshake Type: Client Hello (1)
            Length: 508
            Version: TLS 1.2 (0x0303)
            Random: ba9f24242b401e562d6c529d6ea5cf...
                GMT Unix Time: Mar 20, 2069 07:54:12.000000000 UTC
                Random Bytes: 2b401e562d6c529d6ea5cf631acccb7...
            Session ID Length: 0
            Cipher Suites Length: 32
            Cipher Suites (16 suites)
                Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
                Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303)
...
            Extension: elliptic_curves (len=14)
            Extension: key_share (len=107)
            Extension: supported_versions (len=9)
                Type: supported_versions (43)
                Length: 9
                Supported Versions length: 8
                Supported Versions: TLS 1.3 (draft 18) (0x7f12)
                Supported Versions: TLS 1.2 (0x0303)
                Supported Versions: TLS 1.1 (0x0302)
                Supported Versions: TLS 1.0 (0x0301)
            Extension: psk_key_exchange_modes (len=2)
            Extension: signature_algorithms (len=24)

TLS1.3のCipher Suitesや、supported_versionsを始めとしたTLS1.3の拡張がちゃんとパースされる


同様にServer Hello

Secure Sockets Layer
    TLSv1 Record Layer: Handshake Protocol: Server Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 115
        Handshake Protocol: Server Hello
            Handshake Type: Server Hello (2)
            Length: 111
            Version: TLS 1.3 (draft 18) (0x7f12)
            Random: 218aacf7524e2cc7edbb75f985bf651d1243ec09f638ad05...
            Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302)
            Extensions Length: 73
            Extension: key_share (len=69)
                Type: key_share (40)
                Length: 69
                Key Share extension
                    Key Share Entry: Group: secp256r1, Key Exchange length: 65
                        Group: secp256r1 (23)
                        Key Exchange Length: 65
                        Key Exchange: 041433c8bcef4c30966cb6dbeb9...
    TLSv1.3 Record Layer: Application Data Protocol: http-over-tls
        Opaque Type: Application Data (23)
        Version: TLS 1.0 (0x0301)
        Length: 38
        Encrypted Application Data: a1d0f18331e621b995503baa963613de56a19d546a6abaf4...
...

ちゃんと各種パースされている。

Content-Encoding: aes128gcm とは (RFC8188)

20170624追記
RFC8188 として標準化されました


HTTPリクエスト・レスポンスのボディを暗号化する、「Content-Encoding: aes128gcm」を新しく標準化する「Encrypted Content-Encoding for HTTP」という仕様が議論されております。


提案自体は数年前に行われており、すでにWGドラフトになっています。

Encrypted Content-Encoding for HTTP

この仕様では、HTTPのボディをAEAD_AES_128_GCMで暗号化します。HTTPSと違って通信路だけでなく、アップロードファイル・ダウンロードファイル自体が暗号化されているため、ファイルが保存される場合も鍵を知っている人(ソフトウェア)のみがその内容を読むことができます。


仕様上では上記のような例があげられておりますが、IETFで同様に議論されているPush通知の標準プロトコル Web Push でこの仕様を使用します。「Message Encryption for Web Push」という仕様で、Content-Encoding: aes128gcmを使ってPush通知の暗号化プロトコルが実現されています。

暗号化概要

この仕様では、暗号化されたデータの他にも幾つかのパラメータをHTTPボディに含めて送信します。


具体的には下記のパラメータが暗号データ以外に送信されます

+-----------+--------+-----------+---------------+
| salt (16) | rs (4) | idlen (1) | keyid (idlen) |
+-----------+--------+-----------+---------------+
  • salt: ソルト
  • rs: レコードサイズ
  • idlen: keyidの長さ
  • keyid: keying materialの識別子


特徴として鍵交換の具体的方法は定義されておりません、別途別の手段で交換する必要があります。この仕様ではお互いにどのkeying materialを使用しているか識別するためのkeyidを通知するだけになります。


実際に暗号化に使用する鍵は、keying materialより導出されます。

鍵の導出

送信したパラメータと、keying materialより暗号化に用いる鍵を導出します
input keying material (IKM), pseudorandom key (PRK), content encryption key (CEK)

   PRK = HMAC-SHA-256(salt, IKM)
   cek_info = "Content-Encoding: aes128gcm" || 0x00
   CEK = HMAC-SHA-256(PRK, cek_info || 0x01)

( || は結合)

ノンスの導出

同様にノンスも導出します
The record sequence number (SEQ)

   nonce_info = "Content-Encoding: nonce" || 0x00
   NONCE = HMAC-SHA-256(PRK, nonce_info || 0x01) XOR SEQ

レコードのシーケンス番号がつくことで、順番の入れ替えや欠落を検出できるようになっております。

パディングの順番が変更になりそうですので、その例でどのように暗号化されるか見ていきます。
( https://github.com/httpwg/http-extensions/blob/0f3ff05b3679f9ead35c1be800723e73915b6aaf/draft-ietf-httpbis-encryption-encoding.md )


この例では、暗号化するデータは「I am the walrus」(UTF-8)、keying materialは「B33e_VeFrOyIHwFTIfmesA」(base64)、keyidは空文字列

HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: 54
Content-Encoding: aes128gcm

I1BsxtFttlv3u_Oo94xnmwAAEAAA-NAVub2qFgBEuQKRapoZu-IxkIva3MEB1PD-
ly8Thjg
  • salt (from header) = I1BsxtFttlv3u_Oo94xnmw
  • PRK = zyeH5phsIsgUyd4oiSEIy35x-gIi4aM7y0hCF8mwn9g
  • CEK = _wniytB-ofscZDh4tbSjHw
  • NONCE = Bcs8gkIRKLI8GeI8
  • plaintext = SSBhbSB0aGUgd2FscnVzAg