20180824追記
Loading Signed Exchangesについて記事を書きました
Loading Signed Exchangesの仕様 (WebPackaging) - ASnoKaze blog
20180208追記
Bundled HTTP Exchangesについて記事を書きました
Bundled HTTP Exchanges とは (WebPackagingの議論より) - ASnoKaze blog
20180121追記
署名の仕組みとして Origin-Signed HTTP Exchanges を利用する方向のようです
HTTP/2 クロスオリジン サーバプッシュを可能にする提案仕様 - ASnoKaze blog
20171001追記
IETF99にてユースケースについてまずまとめるべき気というフィードバックが有り、それをうけて「Use Cases and Requirements for Web Packages」が提出されています
Webページを丸ごとパッケージングする、Web Packagingの仕様がIETFで提案されています
オフラインやローカル環境で共有出来るようになっており、主な特徴は
- 証明書及び署名がつけられるため、そのパッケージの真正性が確認できる
- HTTPリクエスト/HTTPレスポンスも含まれている(HPACKを利用)
- サブパッケージが利用でき、複数のオリジンをパッケージング可能
- データはCBORで表現される (RFC 7049 - Concise Binary Object Representation (CBOR))
- Index化されており、オフセットを用いて各リソースにランダムアクセス可能
以上の点で、ZIPで固めて保存するのとはまったく違うことがわかるかと思います。
提案仕様と経緯
6月30日に、GoogleのJeffrey Yasskin氏より「Web Packaging」という提案がIETFにて出されました。おそらく議論はDispatch WGで行われ、今月実施されるIETF99
https://www.ietf.org/meeting/99/
でもオフラインでの議論があるかもしれません。
もともと、Webページをパッケージングする仕様は、W3Ctag「Packaging on the Web」というものがありましたが、そこから署名などの機能を追加し整理した「Web Packaging」という仕様がW3CのWICGで議論されておりました。
このW3C WICGの仕様は、データフォマットが要でありIETFで標準化していくのが適切ではないかとIETFに持ち込まれた形になります。
フォーマット
webpackageのフォーマットは以下の通りである
webpackage = [ magic1: h'F0 9F 8C 90 F0 9F 93 A6', ; 🌐📦 in UTF-8. section-offsets: { * (($section-name .within tstr) => offset) }, sections: ({ * $$section }) .within ({ * $section-name => any }), length: uint, ; Total number of bytes in the package. magic2: h'F0 9F 8C 90 F0 9F 93 A6', ; 🌐📦 in UTF-8. ]
- magic: 先頭と最後に挿入されるMagicコード。意味はない
- section-offsets: 各セクションへのオフセット値が与えられる。必須ではない
- sections: ここにManifestや実際のコンテンツが格納される(manifest, indexed-content)
- length: データ長
manifest及び、indexed-contentは以下で説明します。
manifest
sectionsに格納されるmanifestデータ。必須ではない。
日付、オリジン名、証明書、署名が格納される。また、アレば別オリジンのパッケージをサブパッケージとして読み込むことが出来る。
具体的には以下のパラメータです
- metadata: 日付、及びオリジンが記述される。また、あればサブパッケージの指定
- resource-hashes: 各リソースのハッシュ値
- signatures: 署名
- certificates: 証明書
例
"manifest": { "manifest": { "metadata": { "date": 1(1494583200), "origin": 32("https://example.com") }, "resource-hashes": { "sha384": [ h'3C3A03F7C3FC99494F6AAA25C3D11DA3C0D7097ABBF5A9476FB64741A769984E8B6801E71BB085E25D7134287B99BAAB', ... ] } }, "signatures": [ { "keyIndex": 0, "signature": h'3044022015B1C8D46E4C6588F73D9D894D05377F382C4BC56E7CDE41ACEC1D81BF1EBF7E02204B812DACD001E0FD4AF968CF28EC6152299483D6D14D5DBE23FC1284ABB7A359' } ], "certificates": [ DER( Certificate: ... Signature Algorithm: ecdsa-with-SHA256 Issuer: C=US, O=Honest Achmed's, CN=Honest Achmed's Test Intermediate CA Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) pub: ... ), DER( Certificate: ... ) ] },
indexed-content
sectionsに格納される、indexed-content。ここにコンテンツの中身が格納される。
各リソースに対するHTTPリクエスト及びHTTPレスポンスが合わせて格納されており、ヘッダに関してはそれぞれHPACKでエンコードされている。
例
"indexed-content": [ [ [ hpack({ :method: GET :scheme: https :authority: example.com :path: /index.html }), 1] [ hpack({ :method: GET :scheme: https :authority: example.com :path: /otherPage.html }), 121], [ hpack({ :method: GET :scheme: https :authority: example.com :path: /images/world.png }), 243] ], ], [ [ hpack({ :status: 200 content-type: text/html date: Wed, 15 Nov 2016 06:25:24 GMT expires: Thu, 01 Jan 2017 16:00:00 GMT }), '<body>\n <a href=\"otherPage.html\">Other page</a>\n</body>\n' ] [ hpack({ :status: 200 content-type: text/html date: Wed, 15 Nov 2016 06:25:24 GMT expires: Thu, 01 Jan 2017 16:00:00 GMT }), '<body>\n Hello World! <img src=\"images/world.png\">\n</body>\n' ], [ hpack({ :status: 200 content-type: image/png date: Wed, 15 Nov 2016 06:25:24 GMT expires: Thu, 01 Jan 2017 16:00:00 GMT }), '... binary png image ...' ] ] ]
セキュリティについて
オフラインの利用を想定しているため、証明書の失効確認については難しいところがあります。Webパッケージの利用者は正当性を確認する際は、オンライン時はOCSPを利用して失効確認が可能ですが、オフラインのときはブラウザによって判断されることになるでしょう(1週間だけ有効など)。
そこの部分については今後の議論になるかと思われます。