kanejaku.org

Encrypted Client Helloを設定した

24 Feb 2024

このサイトは自作のHTTP/2サーバで運用している。そのサーバにEncrypted Client Helloを試験的に導入してみた。ChromeであればDevToolsのSecurityパネルでECHが有効になっているか調べることが出来る。

自作HTTP/2サーバはTLSのバックエンドを切り替えられるように実装していて、Rustls, OpenSSL, BoringSSLを使えるようにしている。今のところECHが使えるのはBoringSSLだけなので今回はBoringSSLを使った。手順は以下の通り。

  1. ECHConfigおよび公開鍵暗号の鍵ペアを作る
  2. サーバ側でBoringSSLにECHConfigと秘密鍵を設定する
  3. DNSのHTTPSリソースレコードにECHConfigListを設定する

ECHConfigの作成

TLS Encrypted ClientHello(ECH) を BoringSSLで試してみるに書いてある通りにBoringSSLをビルドしてbssl generate-echを実行してECHConfig、秘密鍵およびECHConfigListの3つを作る。前者二つはサーバへ設定するプライベートなもので、3つめのECHConfigListはクライアントへ公開するためのもの。なおbssl generate-echの使い方はこのコマンドが追加されたコミットのメッセージに書かれている。

サーバの設定

サーバを変更して前段で作ったECHConfigと秘密鍵をBoringSSLのAPIを通して設定する。使うAPIは以下のbsslサーバ実装を参照した。

    bssl::UniquePtr<SSL_ECH_KEYS> keys(SSL_ECH_KEYS_new());
    bssl::ScopedEVP_HPKE_KEY key;
    if (!keys ||
        !EVP_HPKE_KEY_init(key.get(), EVP_hpke_x25519_hkdf_sha256(),
                           ech_key.data(), ech_key.size()) ||
        !SSL_ECH_KEYS_add(keys.get(),
                          /*is_retry_config=*/1, ech_config.data(),
                          ech_config.size(), key.get()) ||
        !SSL_CTX_set1_ech_keys(ctx.get(), keys.get())) {
      fprintf(stderr, "Error setting server's ECHConfig and private key\n");
      return false;
    }

自作サーバはRustで書いているのでこれらのAPIをbinding経由で使う。

DNS HTTPSリソースレコードの登録

サーバ側の準備ができたのであとはクライアント(ブラウザ)へECHの設定を公開する。2024年2月の時点では、FirefoxおよびChromeはECHが使えるかどうかを判断するのに提案されているDNS HTTPSリソースレコードのechパラメータを使っている。このパラメータに最初のステップで作ったECHConfigListをbase64でエンコードした値を設定する。

設定の確認

あとはブラウザからサーバへ接続してECHが有効かどうかを調べる。Wiresharkで見てみるとSNIがbssl generate-echで指定した公開用のドメインになっていて、かつ encrypted_client_hello (0xfe0d, 65037) extensionが送信されているのが分かる。

その他

ECHの概要についてはFirefoxのヘルプCloudflareのブログポストが分かりやすい。

ECHが採用しているHPKEについてはHPKE とは何かが参考になる。