Quick set up guide for Encrypted Client Hello (ECH)

The Encrypted Client Hello (ECH) mechanism draft-spec is a way to plug a few privacy-holes that remain in the Transport Layer Security (TLS) protocol that’s used as the security layer for the web. OpenSSL is a widely used library that provides an implementation of the TLS protocol. The DEfO project has developed an implementation of ECH for OpenSSL, and proof-of-concept implementations of various clients and servers that use OpenSSL, and other TLS libraries, as a demonstration and for interoperability testing. DEfO is funded by the Open Technology Fund (OTF).

This guide is aimed at those who would like to try out ECH with our experimental Debian unstable packages for OpenSSL, curl and nginx. The DEfO project is making packages temporarily available so people can carry out such experiments before upstream maintainers include ECH in standard releases.

This guide describes two set ups: one assumes that you want to setup everything (i.e. a web server and associated DNS authoritative server) on one virtual machine and a second describes a way to set up an ECH-enabled web server if you have an existing DNS configuration elsewhere. Many other configurations are of course possible. Comments/questions are welcome via email or as GitLab issues/merge requests.

In summary this guide shows how to: - find and install our Debian unstable packages - configure a minimal DNS setup (using dnsmasq or bind) that enables use of ECH from browsers - configure a minimal nginx setup for an ECH enabled web sites - test your DNS set up and that ECH is working

We assume the reader is comfortable with simple DNS management and system administration tasks. When commands shown below should be run as root they are prepended with a ‘#’, if not they are prepended with a ‘$’.

Install packages

You need to first have access to a virtual machine running Debian unstable. Many online resources describe many ways to do this.

We start with an empty (minimal) Debian unstable system and install OpenSSL from the DEfO apt repository.

# apt install ca-certificates
# echo "deb [trusted=yes] https://defo.ie/debian/ ./" > /etc/apt/sources.list.d/defo.list
# apt update
# apt install openssl

Pick some DNS names to use

In the text below we assume that the web server for which we wish to benefit from ECH is called hidden.example.com

ECH also involves a so-called public_name - that’s present in the outer ClientHello when ECH is used and hence is visible to a network observer, unlike the server name we include inside the Encrypted Client Hello. The public_name we use below is example.com`

You should obviously substitute your chosen DNS names.

Generate an ECH key pair

For ECH to work, we need a new asymmetric key pair. (Different from those used for TLS server authentication), so next we generate an ECH key pair:

# mkdir /etc/echkeydir/
# openssl ech -public_name example.com -pemout /etc/echkeydir/example.pem.ech

Note that we need this file later to set up DNS as well as nginx. Nginx will make use of the private key from the ECH key file, whilst the DNS will be used to publish the corresponding public key in an HTTPS resource record (that’s where ECH-enabled browsers will look for ECH public keys).

The file name chosen should end with .pem.ech

Set up DNS

There are many ways in which one can set up the DNS records required to enable experimenting with ECH. We document two ways here, the first is where the main DNS name with which you’re experimenting is newly registered DNS name and has no existing DNS set up. The second describes a case where a DNS name exists but we need to add some new resource records to enable ECH.

In both cases, <IP> should be the IPv4 address for the host running the web server. (We leave IPv6 handling as an exercise for the reader, but there’s nothing ECH-specific required for IPv6.)

Standalone/New setup : dnsmasq as a authoritative DNS server to serve the ECH key (on the same VM)

In this case we also need to pick some DNS nameserver names, we assume the authoritative primary nameserver for both web server DNS names is ns.example.com with a secondary of ns2.example.com`

The DNS set up to use starting from a clean dnsmasq install:

# apt install dnsmasq
# cat <<EOF > /etc/dnsmasq.d/example.conf
dns-rr=hidden.example.com,65,<ascii-hex encoded HTTPS rdata>
# systemctl restart dnsmasq

Note that you need to bump the SOA version (42 in the above) every time you change the config and make sure the zone distributes to ns2.

The <ascii-hex encoded HTTPS rdata> will be published as the HTTPS resource record (type == 65) for hidden.example.com and can be produced from the file we generated earlier via a shell script you can download and use as follows:

$ _curl_ https://raw.githubusercontent.com/sftcd/openssl/ECH-draft-13c/esnistuff/pem2rr.sh -o pem2rr.sh
$ chmod u+x pem2rr.sh
$ ./pem2rr.sh /etc/echkeydir/example.pem.ech

Let’s assume you have an existing bind-based DNS setup for example.com, but are moving the IP address for that to a new VM that'll run the ECH-enabled web server for bothexample.comandhidden.example.com`. The change you might then make using the bindnsupdate` command on the authoritative DNS server would then look like:

  • Change/add IP address records for our DNS names.
$ sudo nsupdate -l
> update delete example.com a
> update delete example.com aaaa
> update add example.com 300 a <IP>
> update add hidden.example.com 300 a <IP>
> send
> quit
  • Add new HTTPS resource record for hidden.example.com

First we need to get the base64 encoded public from our /etc/echkeydir/example.pem.ech file:

$ tail -2 /etc/echkeydir/example.pem.ech | head -1

That base64 encoded value is what we need to publish in the DNS…

$ sudo nsupdate -l
> update delete hidden.example.com HTTPS
> update add hidden.example.com 300 HTTPS 1 . ech=<base64-encoded-value>
> send
> quit

Checking your DNS set up

You can check that value is correctly published in the DNS e.g. using the dig command`:

$ dig +short https hidden.example.com

If your version of dig is older and doesn’t know about HTTPS resource records, then you may need to check via:

$ dig +short +unknownformat -t TYPE65 hidden.example.com
\# 67 0001000005003C003AFE0D0036990020002005EDC2D3F8AB4AD28476 0EB4421979B30563357878098031062EA2FAE38FCE70000400010001 0007686F62612E69650000

The 2nd-last invocation of dig above shows the presentation format version of the HTTPS resource record. The last invocation above shows the equivalent in ascii-hex, which (minus the spaces) is what dnsmasq needs in it’s configuration file.

We assume dnsmasq will likely accept presentation format for HTTPS resource records in future as bind tooling already.

Set up nginx to serve an outer and an inner (hidden) website

Install nginx-light from the DEfO repo, as above:

# apt install _nginx_-light certbot python3-certbot-_nginx_

If you don’t already have a web server config for example.com then you can create a basic pair of sites-enabled configuration files via the following commands:

# rm /etc/_nginx_/sites-enabled/default
# cp /etc/_nginx_/sites-available/default /etc/_nginx_/sites-enabled/example.conf
# sed -i 's/server_name _;/server_name example.com;/' /etc/_nginx_/sites-enabled/example.conf
# echo "ssl_echkeydir /etc/echkeydir/;" >> /etc/_nginx_/sites-enabled/example.conf
# cp /etc/_nginx_/sites-available/default /etc/_nginx_/sites-enabled/hidden.example.conf
# sed -i -e 's/server_name _;/server_name hidden.example.com;/' -e sed 's/\(listen.*\)default_server;/\1;/' /etc/_nginx_/sites-enabled/hidden.example.conf
# systemctl restart _nginx_

It’s possible the sed commands above may not work for you, e.g. if default files change, or perhaps you won’t use those if you have an existing web server config for example.com. In that case you can simply edit the config files to ensure the following, before runningcertbot``:

  • you have example.com and hidden.example.com servers set up with correct server_name`
  • optionally change the DocRoot (root) in these configuration files to serve different content
  • there’s a line within the http stanza as follows: ssl_echkeydir /etc/echkeydir/;`
  • the ssl_echkeydir line can in _nginx_.conf within the http stanza or within either of the files in sites-enabled when it must be outside the server stanza

Run certbot to get a TLS server certificate

Next you need to rRun certbot to get TLS server public keys certificates for both DNS names (example.com` and `hidden.example.com). Those can be in the same certificate (or not) for the purposes of this experimental setup.

# certbot --nginx

certbot may prompt you for e.g. an email address or approval and if you have an existing certificate for example.com you may be prompted as to whether you want to add hidden.example.com to that certificate.

Use curl to test the set up

Install our ECH-enabled curl package from the DEfO repo and run:

$ curl -v --ech true --doh-url https://hidden.example.com/ |& grep Succeeded

You should see this in the output:

ECH: result: status is Succeeded, inner is hidden.example.com, outer is example.com

Check with browsers

If you’re running a recent browser version (chromium-based since version 105, firefox since before then:-) you can enable ECH in the browser by following our client configuration instructions. Unfortunately, browsers don’t provide user interface to show if ECH has succeeded or not, but you can verify that your browser is able to use ECH if you visit https://defo.ie/ech-check.php.


All going well, you should now have an ECH-enabled web site and be able to extend/play with that as you like. We’d appreciate feedback on this guide if you have a chance. As before, comments/questions are welcome via email or as GitLab issues/merge requests.