TLS Encrypted ClientHello(ECH) を BoringSSLで試してみる

TLS Encrypted ClientHello(ECH) を BoringSSLで試してみます。

なお、現時点ではBoringSSLはdraft 10版の ECH 対応です。

TLS Encrypted ClientHello(ECH) について

TLSの通信のうち、クライアントからサーバに送信されるSNI(Server Name Indication)は、通信先のホスト名が記載されます。

これは平文で送信されるため、通信の観測者はどのドメインと通信しているか分かります。これを秘匿するために Encrypted SNI (ESNI)という仕様が議論されています。

今は、TLS Encrypted ClientHello(ECH)という形で標準化が進められており、Firefoxなどが既に対応しています。

例えばこのECHを使うと、クライアントは「public.example.com」と「private.example.org」をサーバに送りますが、private.example.orgの情報は暗号化されて送れます。CDNやマネージドロードバランサが対応し、外に見えるSNIはCloudflareやクラウドベンダーのドメインとなれば、見えるSNIから暗号化されたSNIを観測者が推測するのも難しくなるでしょう。
f:id:ASnoKaze:20210723222220p:plain

古いですがもう少し詳しくは昔書いた通り。
asnokaze.hatenablog.com

実装を触ってみる

すでに幾つかの実装があり、相互接続性のテストを行っております。
https://github.com/tlswg/draft-ietf-tls-esni/wiki/Implementations

今回はBoringSSLで通信の様子を見ていきます。

ビルドする

説明に基づいてビルドします
https://boringssl.googlesource.com/boringssl/+/HEAD/BUILDING.md

ECH設定の生成

generate-ech で ech_config_listを生成します。今回は public-nameとしてpublic.example.comを指定します。

$ ./build/tool/bssl generate-ech  -out-ech-config-list ech_config_list.data \
   -out-ech-config ech_config.data \
   -out-private-key ech.key \
   -public-name public.example.com \
   -config-id 0

この ech_configにはHpkePublicKeyやpublic_nameが格納されます。

一般的に、クライアントはこのech_configはDNS HTTPS レコードを介して取得します。(DNSの通信はDoHなどで暗号化しておけば、通信の観測者はどのドメインHTTPSレコードを取得している事もわかりません)

asnokaze.hatenablog.com

通信してみる

サーバを起動しておく

$ ./build/tool/bssl server -accept 4430 \
    -ech-key ech.key \ 
    -ech-config ech_config.data 

クライアント側でつなぐ

 $ ./build/tool/bssl client -connect localhost:4430 \
    -ech-config-list ech_config_list.data \ 
    -server-name private.example.org

Connecting to 127.0.0.1:4430
Connected.
  Version: TLSv1.3
  Resumed session: no
  Cipher: TLS_AES_128_GCM_SHA256
  ECDHE curve: X25519
  Signature algorithm: ecdsa_secp256r1_sha256
  Secure renegotiation: yes
  Extended master secret: yes
  Next protocol negotiated: 
  ALPN protocol: 
  OCSP staple: no
  SCT list: no
  Early data: no
  Encrypted ClientHello: yes
  Cert subject: C = US, O = BoringSSL
  Cert issuer: C = US, O = BoringSSL

サーバ側の出力

$ ./build/tool/bssl server -accept 4430 \
    -ech-key ech.key \
    -ech-config ech_config.data 

Connected.
  Version: TLSv1.3
  Resumed session: no
  Cipher: TLS_AES_128_GCM_SHA256
  ECDHE curve: X25519
  Secure renegotiation: yes
  Extended master secret: yes
  Next protocol negotiated: 
  ALPN protocol: 
  Client sent SNI: private.example.org
  Early data: no
  Encrypted ClientHello: yes

サーバ側からするとEncrypted ClientHelloがyesになっている。また、クライアントが送った本来のSNI private.example.org が認識できています。

また、crypto.cloudflare.com がECHに対応していますが、HTTPS DNSレコードからECH Config listを取得すれば、同様にEncrypted ClientHelloが通信できました。

パケットキャプチャしてみる

上記で行った通信をパケットキャプチャしてみます。

経路上の観測者としては、ESNIは「public.example.com」しかみえません。但し、暗号化されている Enclipted Clienthelloが格納されている encrypted_client_hello 拡張 (65034 == 0xFE0A (ECH Draft-10)) がついている事が分かります。

f:id:ASnoKaze:20210723224028p:plain