QUIC over DTLSの提案仕様

2021/12/21 追記
この記事は古いです。2018年、QUICの標準化において設計段階で行われた議論になります。


QUIC over DTLS」という提案仕様ekr氏が出され、QUIC WGのメーリングリストで「Proposal: Run QUIC over DTLS」としてDTLS上でQUICのメッセージを通信するように変更する提案がされている。

QUICのスタック

現在のQUICのスタックは図のようになっている。
f:id:ASnoKaze:20180306233002p:plain

QUICが提供する信頼性のあるトランスポート上のStream 0でTLS1.3のハンドシェイクのメッセージ(Crypto Handshake)をやりとり、そこから得れられたシークレットから鍵を生成し通信を暗号化します。

詳細は、tatsuhiro-t先生の記事が詳しいです。
qiita.com

しかし、ekr氏の提案で述べられているように、Crypto HandshakeはStream 0に特殊なルールを適用することになっていたり、0-RTTやACKに関するルールを複雑化していると述べています。また、QUICとTLSスタックを密結合にしているとも書かれています。

提案されている 「QUIC over DTLS」では、QUICのスタックは以下のようになります。
f:id:ASnoKaze:20180306234235p:plain

DTLSのハンドシェイクを行った後、その上でQUICのメッセージをやり取りするようになります。
それに伴い、この提案仕様でQUIC部分も変更される部分が出てきます。

  • QUICのVersion Negotiationの変更 (DTLS1.3のsupported_versionsに加え quic_versionsのネゴシエーション)
  • Transport ParametersはTLS拡張へ
  • DTLSの提供するACKと、QUICの提供するACKが分離する

もちろん、QUICとDTLSスタックとやり取りする必要もあるし、そのままDTLSを利用できない部分もあります。

DTLSの変更

QUICの仕様も既に長い間議論されてきており、沢山の事が考慮され現在の形になっています。

そのような様々な考慮事項を満たすために、DTLSはそのまま使用できません。提案仕様の「3. Required Changes to DTLS」にかかれているとおり、幾つか変更する必要があります。

  • DTLS1.3 Connection ID: 別途議論されているDTLSにコネクションIDを導入する拡張の仕様 (「DTLSにコネクションIDを導入する提案仕様」)
  • ハンドシェイクメッセージの難読化
  • ネゴシエーションパケットの難読化
  • パケット番号の暗号化
  • Stateless Reset: サーバがリブートなどして、コネクションの状態情報を失った時に接続をリセット する手段を提供する

各種メッセージの難読化については、以前書いたとおり
asnokaze.hatenablog.com

実装

ekr氏は既に「QUIC over DTLS」を実装しています。
github.com

議論

QUIC WGのメーリングリストで、議論は盛り上がっています。

やはり大きな変更となるため、興味を持つものの好意的でない意見の方が多い印象です。今まで沢山の議論を重ね、GQUICからの知見も得て、QUICは今の仕様になっています。今のデザインのまま、まだまだ改善できる余地はあるのではないかという感じでしょうか。

今月ある IETF101のアジェンダにも既に含まれています。議論となることでしょう。
github.com

HTTP/1.1 (RFC 7230 〜 7235) の改訂作業がはじまる

2021/10/21 追記。変更点について別記事を書きました。
asnokaze.hatenablog.com


HTTP/1.1の仕様は下記の通り、6つのRFCで標準化されています。

これら、HTTPリクエスト・レスポンスヘッダや、ステータスコード、キャッシュや認証の仕組みといったHTTPの意味(セマンティクス)はHTTP/2でもHTTP over QUICでも変わっていません。

つまり、引き続きHTTPのセマンティクスを定義しているこれらのドキュメントは整備(メンテナンス)し続ける必要があります。

また、今回の作業によりHTTP/1.1のシンタックスと、HTTPのセマンティクスの仕様を分離することで、HTTP/3などからはセマンティクスの仕様を参照でき、仕様としてより整理された形となります。

RFCは一度出てしまうと変更することは出来ず、errataと言うかたちで訂正が蓄積していきますが、どこかで改訂版を出す必要が出てきます。上記RFCが出て4年が経ち既にIssueはいくらか溜まっており、その一覧はhttpbis WGのgithubリポジトリで確認できます。現状では48個のissueが登録されています。
github.com

そこで、IETFのhttpbis WGでは、次のHTTP標準の改訂作業が行われる機運が高まっており、前々回のIETF99(2017年11月頃)でもその議論が行われています。発表資料は以下のより確認できる。
github.com

HTTP/1.1は既に一度改定しており、その時は次のHTTPと言う意味で、HTTPbisと呼ばれていました。そして3回目の改訂としてHTTPtreと呼ばれるのが今回のドキュメントです

  • 最初: RFC 2616
  • HTTPbis: RFC 7230 〜 7235
  • HTTPtre: 次のドキュメント

(ちなみに、WGの名前はhttpbisのままで行くようです)

HTTPtre

本日、そのHTTPtreのドラフトが提出されました。Authorは、HTTPbisの改訂でも根強く尽力されたJulian F. Reschke氏と、Roy T. Fielding氏です。

それぞれ、RFC 7230 〜 7235と対応して、各改訂版は下記のとおりです。

Message Syntax and RoutingはHTTP/1.1固有のものですが、それ以外はHTTPとしてのセマンティクスを定義しているため、ドキュメントタイトルから1.1という部分がなくなっています。

これらのドキュメントは、個人ドラフトの00版ですがすでに変更が入っており、各ドキュメントの最後に変更点が記述されています。

HTTPtreのスコープとスケジュール

HTTPtreの作業項目と、そのドラフト更新の目処については、チェアのmnotが去年の段階で言及しています。
DRAFT: more details for HTTPtre

それを見るとHTTPbis(6年かかった)に比べれば、短い期間で改訂作業が進められそうです。

まずは、このドキュメントを元に、今月行われるIETF101で議論を行い、これから編集作業が進んでいくものと思われます。

追記: ドキュメントは3つに統合

HTTPtreよりも、HTTP Core Documentsと呼ばれるようになりました。

現在は、3つの仕様に統合され、HTTPのセマンティクス、HTTP/1.1のフォーマット、キャッシュに関する3つの文書に整理されています。

QUICにおけるヘッダ圧縮の提案仕様 QPACK(旧QCRAM) その2 (draft-04)

20180313追記
QCRAMと呼ばれていた仕様は、QPACKに改称されました。
github.com


HTTP over QUICでは、HTTP/2のフレームを利用するが、HTTPヘッダ圧縮にHTTP/2のHPACKをそのまま使用するのはHoLBの問題が知られている。

HPACKでは、ヘッダが送った順番通りに届く事を想定しており、送信側と受信側が完全に同じ状態を保てる。しかし、QUICではメッセージの届く順番は入れ替わる可能性があり、動的テーブルの参照する際にまだそのインデックスが挿入されていないというケースが生じうる。QUICでは、届いたメッセージから処理できるはずが、ここで待ち状態になってしまう。

そこでQUICでのHTTPヘッダ圧縮は新しい仕組みを導入することが検討されており、幾つか提案されているなかの一つが「Header Compression for HTTP over QUIC」であり、QCRAMと呼ばれている。

そのQCRAMが Call for Adoption となっている、またGo実装でもあるminhqでもQCRAMが試験的に実装されている。IETF101に向けて、事前に仕様を読んでおく。

今回は、前回のdraft-02からのアップデートのみ触れる。詳しくは前回を参照のこと。
asnokaze.hatenablog.com

draft-04

Call for Adoption となっている、現在のQCRAMはdraft-04であり、draft-02から幾つかの変更点がある。

主な変更点である、下記について書く

  • HEADER_ACK
  • BLOCKING flag
  • ヘッダブロックプレフィックス
HEADER_ACK フレーム

新しく HEADER_ACKフレームが定義される。これは、デコーダ側がヘッダブロックを処理した際にControl Streamでエンコーダ側へ通知される。このHEADER_ACKフレームをエンコーダ側が受信することで、送ったヘッダがどこまで処理されてるか判別することが出来るようになる。

f:id:ASnoKaze:20180212221216p:plain

  • Stream ID: ヘッダブロックを処理したストリームID(可変長)

trailersなどで複数回ヘッダブロックを処理する可能性があるが、おそらくその都度送信される。

BLOCKING フラグ

HEADERSフレームとPUSH_PROMISEフレームにBLOCKING フラグが追加されます。

前述のHEADER_ACKによってエンコーダ側は送信したヘッダブロックがどこまで処理されているかを知ることが出来ます。そこで、ヘッダブロックを送信する際に、そのデコーダ側がヘッダブロックを処理するのにブロックされうるのかが分かります。

このフラグが0の場合は、デコーダ側はすぐにそのヘッダブロックを処理できます。1の場合は、依存するヘッダブロックが処理されるまで待つ可能性があります。

ヘッダブロックプレフィックス

以前の記事で説明したとおり、QCRAMではヘッダブロックにプレフィックスが追加されます。このプレフィックスもdraft-02から変更されています。

このヘッダブロックプレフィックスは、BLOCKING フラグによって変わります。
BLOCKINGフラグが0の場合が左、BLOCKINGフラグが1の場合は右です。


f:id:ASnoKaze:20180212231947p:plain

  • Base Index: 現在のヘッダブロックを初果する前の、追加されたエントリの合計
  • Depends: Base Indexの位置よりどれくらい前のインデックスに依存するか。Base Index - Dependsが依存する最大のインデックス値になります。

HPACKではエントリを挿入するたびにインデックス値がずれていきましたが、Base Indexから場所を計算することで順番通りでない挿入でも正しい場所にエントリを挿入できます。

Dependsがあることで、ブロックの処理を開始する前に必要なインデックス値を確認できるようになっています。

NginxがHTTP2サーバプッシュに対応したので試す

追記
[nginx-announce] nginx-1.13.9
http://mailman.nginx.org/pipermail/nginx-announce/2018/000207.html

1.13.9でサーバプッシュがサポートされました


先程、Nginxでサーバプッシュをサポートするコミットが入ったので試す。

HTTP/2: server push.
http://hg.nginx.org/nginx/rev/641306096f5b

ビルド

$ git clone https://github.com/nginx/nginx.git
$ cd ./nginx

$ ./auto/configure  --with-http_ssl_module --with-http_v2_module
$ make
$ sudo make install #一旦インストールしてしまう

設定

一旦 h2cで設定する。

$ sudo vim /usr/local/nginx/conf/nginx.conf
    server {
        listen       80 http2; #h2を有効化
        server_name  localhost;

        http2_push_preload on; #preload方式のプッシュを有効化
        location / {
            root   html;
            add_header "Link" "</test.js>;rel=preload"; #Linkヘッダを付ける
            index  index.html index.htm;
        }
    }
  • http2_push_preload onとすることで、Linkヘッダでpreload指定されたファイルをプッシュするようになる (リバースプロキシでバックエンドから送っても良い)
  • 今回は、add_header ヘッダで直接Linkヘッダを指定

preloadの詳しい仕様は
https://w3c.github.io/preload/ 参照

試す

$ sudo ./objs/nginx #起動

#nghttpで確認する。適宜インストールしてね
$ nghttp http://localhost -vn
....
[  0.004] recv (stream_id=13) :method: GET
[  0.004] recv (stream_id=13) :path: /test.js
[  0.004] recv (stream_id=13) :authority: localhost
[  0.004] recv (stream_id=13) :scheme: http
[  0.004] recv PUSH_PROMISE frame <length=23, flags=0x04, stream_id=13>                                                                                                 

ちゃんと、PUSH_PROMISEが送られている。

良さそう。

ちなみに、Apacheの場合はこちら
asnokaze.hatenablog.com

Bundled HTTP Exchanges とは (WebPackagingの議論より)

20190709追記
draft 01が出ました。幾つかの修正が入っています。そのため、本記事は内容が古くなっています。
https://tools.ietf.org/html/draft-yasskin-wpack-bundled-exchanges-01

20180615追記
Bundled HTTP Exchangesの仕様がIETFに提出されました
https://tools.ietf.org/html/draft-yasskin-wpack-bundled-exchanges-00


WebPackagingという仕様が議論されている。簡単な概要は以下の記事に書いたとおり
asnokaze.hatenablog.com

WICG/webpackageで書かれている通り、WebPackagingは以下の2つに分離して標準化が進められそうです

  • Signed HTTP exchanges: HTTPリクエスト/レスポンスをブラウザが信頼できるように署名する仕組み
  • Bundled HTTP exchanges: 複数のHTTPリクエスト/レスポンスを一つのCBOR形式で表現できるようにする仕組み


Signed HTTP exchangesの方は、既にIETFに提案しようが出ており下記の記事で軽く触れた。
asnokaze.hatenablog.com


Bundled HTTP exchangesの方は、未だIETFには提出されていないが、著者様の個人リポジトリにdraftがあるため軽く読んでおく。
(まだIETFに提出されていないため変更される可能性があります)

Bundled HTTP exchanges

仕様は「https://jyasskin.github.io/webpackage/bundles/draft-yasskin-dispatch-bundled-exchanges.html」から確認できる

bundleは複数のHTTPリクエストレスポンスを一つにまとめる。データ構造はCDDLで記述する。

bundle = [
  ; 🌐📦 in UTF-8.
  magic: h'F0 9F 8C 90 F0 9F 93 A6',
  section-offsets: {* (($section-name .within tstr) => uint) },
  sections: [* $section ],
  length: bytes .size 8,  ; Big-endian number of bytes in the bundle.
]

$section-name /= "index" / "manifest" / "critical" / "responses"

$section /= index / manifest / critical / responses

responses = [*response]

まず最初にメタ情報のロードが行われる

  • bundleは🌐📦であるmagicから開始される
  • 続いて、0以上のsection-offsetsが続く。
    • section-offsetsのkeyは、"index", "manifest", "critical", "responses"のどれかである (未知のセクションは無視される)
    • section-offsetsのvalueは、それらのセクションが何バイト目から開始されるかのオフセットである。
  • sectionは各セクションの構造毎にパースされる
    • responsesは実際にデータをロードする際にパースされる。
  • lengthは、このbundleの長さ

indexセクション

HTTPリクエストに対応するレスポンスが、responsesセクションのどこに存在するかのインデックス。responsesセクション開始からのオフセットと長さが与えられる。

index = {* http-request => [ offset: uint,
                             length: uint] }

http-requestは下記の構造で表現される

http-request = {
  * bstr => bstr
}

manifestセクション

bundle自身を識別するためのマニフェストファイル(Web App Manifest形式)へのHTTPリクエスト情報。

manifestセクションはhttp-requestからのみなる

manifest = http-request

詳しくは (Web App Manifest形式)

criticalセクション

将来のバージョンを想定して、未知のセクションは無視されるが、サポートすべきセクション名を記述できる。

criticalセクションは以下の通り

critical = [*tstr]

ココに書かれているsection-nameをサポートしていない場合はパースは失敗となる。

responseセクション

responseセクションは下記の通りである

response = [headers: {* bstr => bstr}, payload: bstr]

各responseはレスポンスヘッダと、HTTPボディであるペイロードからなる。ヘッダとしてステータスコードを示す":status"を含めなければなりません。

QUICの現状確認をしたい 2018/2 (MTU, Migration, Packet Number Encryptionなど)

3月分も書きました
asnokaze.hatenablog.com


QUICの標準化状況に関して、幾つかトピックを取り上げる

シリーズ化するつもりは無いが、1月に書いたので、今回は2月初旬版
qiita.com

拾いきれてないトピック沢山有るので、皆さんも是非補足して頂ければ

目次

Call for Adoption

1月に行われたQUIC Interimの議論を経て、2つのドキュメントがCall for Adoptionとなっている

QUICのHTTPヘッダ圧縮については幾つか提案があり、ながく議論されていたがその一つがAdoptionされることは追う側としては喜ばしい。WG Draftが出たら読み直そうと思う

詳しくは以前書いた記事を参考に

接続性テスト(Interoperativity)

QUICにおいても、標準化段階から実装を持ち寄って接続性テストを行うことで仕様の確度を高めている。

1月に行った3rd Implementation Draftinterop結果は以下の通り表にまとめられている。
docs.google.com


今後は4th Implementation Draftinteropが実施される

Interim Meeting

オフラインの中間ミーティングが、1月メルボルンで実施された。


アジェンダは下記の通り

  • Summary of interop meeting (EKR)
  • Status update from editors (Martin)
  • QUIC Invariants (Martin)
  • HTTP compression (Mike)
  • Multiplexing with other UDP protocols (Jana)
  • Greasing (Martin)
  • Connection ID and Handshake review (Ian)
  • Connection migration (Jana)
  • Loss recovery (Ian)
  • QUIC Application Abstractions (Jeff / Roberto)
  • ECN in QUIC (Ingemar, morning time MEL preferred)
  • 3rd implementation draft
  • Next Steps (Mark / Lars)

資料と議事録はGithubリポジトリより確認できる。
github.com

QUIC MTU

QUICのトランスポート draft-09にて、Path Maximum Transmission Unit (PMTU)の記述が増えた。

まず、最初のパケットは1200 octet以上になるように決められた。これは、反射型DDoS攻撃を緩和するためである。UDPだと送信元IPを攻撃対象のものに偽装することで、最初のパケットへの応答を攻撃対象に送信させることが出来る。QUICでは最初のパケットを大きくすることでデータ量を増幅できないようになっている。

クライアントがもし経路がそれ以上でも疎通できることを知っていれば、それ以上のサイズの初期パケットを送信しても良い。

PMTUは、経路上で疎通可能なIPヘッダ + UDPヘッダ + UDPペイロードの最大サイズです。QUICパケットはこのPMTUに収まるように送信すべきです。そのため、Path MTU Discoveryを使うべきです。

これらの仕組みを使用せず、1280 octetより大きいパケットを送信すべきではありません(IPv6で保証される、最小PMTU)。

また、IPv4のPath MTU Discoveryには脆弱性が知られているため、そのための緩和策を取るよう書かれています。

Migration

QUICでは、QUICのレイヤでコネクションを管理しているため、IPやポートが変わっても切断されません。NAT rebindingによってIPが変わっても大丈夫ですし、明示的に4G回線からWi-FI回線に切り替える事もできます。このコネクションのマイグレーションに関して、専用のフレームが用意された。

その変更が先日マージされた。
github.com


PATH_CHALLENGE, PATH_RESPONSEというフレームタイプである。
f:id:ASnoKaze:20180206001133p:plain


両方共、Data領域のみを持ちます。新しい経路に切り替える際、推測されにくい値を格納したPATH_CHALLENGEを新しい経路で送信します、それを受け取ったピアは任意の経路で同じ値を持つPATH_RESPONSEを送り返します。このときアドレストークンの検証もちゃんと行います。

こうして経路の確認が取れた後にコネクションをマイグレーションすることが出来ます。

17 octet connection ID

QUICのコネクションIDはサーバから払い出すことが出来ます。この時、そのコネクションに関する情報、例えばそれを払い出したサーバの識別情報を暗号化してコネクションIDに含める事ができます。そうすることで、そのコネクションIDをもつクライアントはそのサーバで処理するといった事ができます。

この使い方は「Manageability of the QUIC Transport Protocol」の4.3でも触れられています。


さて、コネクションIDはもちろんクライアント側から任意の値を使用しようとできるので、サーバ側から発行するコネクションIDに何かしらの情報を埋め込む際は暗号化する必要があります。その時に問題になるのがコネクションIDの長さです。

認証付き暗号を使用のに、128bit長にすることが提案されています。また、使っている鍵の識別子も付け加えて、系17octetになります。
github.com


だたし、可変長コネクションIDという意見も出ており、今後も議論が続くかと思います。

Packet Number Protection

下記記事でも触れているとおり、ファイアウォールなどの中間装置がパケットを読み正しくない挙動を起こす問題が知られています。そのため、そもそもそれらのパラメータを見えないようにするという議論が進んでいます。
asnokaze.hatenablog.com

さらに暗号化を進め、パケット番号も暗号化しようという議論が出ています。

秘密値から導出されるpn_keyと、パケットのデータ領域を暗号化した後に、暗号化領域からサンプリングしてきた値を暗号へのインプットとしてパケット番号を暗号化します。


AES-BasedとChaCha20-Basedがありますが、AES-Basedでは下記のとおりです。

encrypted_pn = AES-CTR(pn_key, sample, packet_number)

EXTENSION Frames

QUICでも拡張フレームを定義する議論が出ています。
github.com

フレームタイプ0x0fで、さらにExtension Typeを持ちます。未知の拡張タイプは無視されます
f:id:ASnoKaze:20180206155611p:plain

QUIC IN ECN

QUICにおけるECNは「ECN support in QUIC」としてInternet-Draftが出ていますが、WikiECN in QUICで編集が進められています。


ECNは詳しくないので、正直良くわからないのですが、「Suggested additions to 'to become' RFCs」と書かれている通り、トランスポート及びロスリカバリのドキュメントに追記されECNサポートへ向かうようだ。

Unbound Server Push (USP) for HTTP/QUIC

メーリングリストで話題になっていないのと、V1のスコープ外なのでどうなるかわからないが、「Unbound Server Push (USP) for HTTP/QUIC」という提案が出ていた。


HTTP/2サーバプッシュは、HEADERSフレームを受けてそれと関連するレスポンスをプッシュすることが出来る。つまり、まずHEADERSフレームを受け取る必要があるが、上記の提案ではそれがなくてもプッシュできるUnbound Server Pushを提案しているようだ。

Application-Layer TLS の標準化動向

IETFでApplication-Layer TLSの議論が行われ始めているので、雑に書き留めておく

Application Transport LAyer Security (Atlas)

前回、アプリケーションレイヤでTLSを喋る「HTTP over TLS」について、記事を書いた。
asnokaze.hatenablog.com


その後、IETFでは、Application Transport LAyer Security (Atlas) というメーリングリストが開設され、WG化のためのチャータの議論が行われています。


以前取り上げたHTTP over TLSユースケースの他にも

f:id:ASnoKaze:20180201082004p:plain
f:id:ASnoKaze:20180201084938p:plain

例えばIoT機器などのデバイスが、Zigbeeであったり、CoAP/DTLS などでゲートウェイと通信し、ゲートウェイからプロトコルを変えインターネットに出ていくようなケースにおいても、エンドツーエンドで暗号通信したい場合があります。その際にエンドツーエンドでの鍵交換の仕組みを新しく作るのではなく、アプリケーションレイヤでTLSのやり取りを行い、鍵交換を行い、中間装置の影響を受けずに暗号通信できるようにします。


まだ、チャーターの議論が始まったばかりで、たたき台(URL)はあるものの、これからいろいろな人の意見を受けて変わるでしょう。ただ、IoT関連を強く意識しているようです。


いまのところでは、下記の提案仕様をWGアイテムとして扱うところを目指し


さらに、大津先生が書かれていたGoogleのATLSもスコープに含まれているようです。
しかし、紛らわしいですが今あるIETF側のドキュメント類はGoogle ATLSとは別物です。
jovi0608.hatenablog.com


2018年 秋頃にユースケースやApplication-Layer TLSのドキュメントをIESGに送る事を考えているようです。

Application-Layer TLS

ATLS (WGとしてATLASだけど、プロトコルはATLSと描かれる)に関するドキュメントとして、下記の2つがありました

  • draft-friel-tls-over-http-00
  • draft-tschofenig-layered-tls-00

これらはマージされて、「Application-Layer TLS (ATLS)」draft-friel-tls-atlsとなりました。それぞれのユースケースを想定し、ATLS over HTTP, ATLS over CoAP について記述されています。


接続の確立時の、クライアント・サーバの各コンポーネント間のやり取りは下記のとおりです。
AppはATLSコンポーネントTLSメッセージを作らせ、使用するトランスポート上で送信します。そのようにしてTLSハンドシェイクを行います。
f:id:ASnoKaze:20180201083853p:plain