このサイトは自作のHTTP/2サーバで運用している。そのサーバにEncrypted Client Helloを試験的に導入してみた。ChromeであればDevToolsのSecurityパネルでECHが有効になっているか調べることが出来る。
自作HTTP/2サーバはTLSのバックエンドを切り替えられるように実装していて、Rustls, OpenSSL, BoringSSLを使えるようにしている。今のところECHが使えるのはBoringSSLだけなので今回はBoringSSLを使った。手順は以下の通り。
- ECHConfigおよび公開鍵暗号の鍵ペアを作る
- サーバ側でBoringSSLにECHConfigと秘密鍵を設定する
- 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 とは何かが参考になる。