Subject: [TLS] ESNI interoperability questions

From: Rob Sayre <[email-masked]>
Date: Sat, 26 Oct 2019 15:31:49 -0700

Hi,

I think I have a working ESNI client, but I'm encountering a strange error
testing with Cloudflare.

I initially tested with "cloudflare.com", but found this was a bad idea,
because that host doesn't seem to require an SNI or ESNI. So, a bogus ESNI
triggered no errors.

When my client sends an ESNI to a Cloudfront-fronted domain, I get a
handshake_failure error (40). According to the -02 draft, this should only
happen if the server fails to negotiate TLS 1.3. I've got my client
configured for TLS 1.3 only, so this shouldn't be an issue. When I add an
unencrypted SNI to an otherwise identical ClientHello, everything works
over TLS 1.3. If there are problems with my ESNI encryption, I should see
other errors. Things like "illegal_parameter" or "decrypt_error", right?

In Wireshark, I can at least see that my encrypted_server_name extension
matches Firefox's cipher and key share entries, and the lengths of
record_digest and encrypted_sni are the same. Firefox does send some
extensions I don't, like ALPN. Does the absence of unencrypted SNI imply
the presence of other extensions?

I also wondered about extension order. Since the ClientHello.key_share is
part of the ESNI calculation, does it need to appear first in the
extensions list?

thanks,
Rob

---

From: Rob Sayre <[email-masked]>
Date: Sat, 26 Oct 2019 19:43:38 -0700

As an update,

I seem to be able to negotiate a handshake with "only.esni.defo.ie", but I
must be making a mistake in my ClientHello. Wireshark sees Firefox's
ClientHello as TLS 1.3, but mine only shows up as TLS 1.0, although the "
only.esni.defo.ie" ServerHello does show up as TLS 1.3. You check out the
details on that host at https://defo.ie/ .

My client fails to read application data after that, although the server
does seem to send it.

You can check out my fork of Rustls here:
https://github.com/grafica/rustls

If you then do something like:
$ cd rustls/rustls-mio/
$ cargo run --example esniclient

you should see some ESNI traffic. Some of the code in the fork is a little
messy so far, but it's still in the "make it work" phase. :)

thanks,
Rob

---

From: Rob Sayre <[email-masked]>
Date: Sun, 27 Oct 2019 14:33:45 -0700

Hi,

This code works with medium.com (hosted by Cloudflare). I was previously
testing with a parked domain. Maybe there's some signaling needed when the
handshake fails based on Split Mode / Shared Mode configuration.

thanks,
Rob

On Sat, Oct 26, 2019 at 7:43 PM Rob Sayre <[email-masked]> wrote:

> As an update,
>
> I seem to be able to negotiate a handshake with "only.esni.defo.ie", but
> I must be making a mistake in my ClientHello. Wireshark sees Firefox's
>> I think I have a working ESNI client, but I'm encountering a strange
>> error testing with Cloudflare.
>>

---

From: Rob Sayre <[email-masked]>
Date: Sun, 27 Oct 2019 20:46:01 -0700

Hi,

The joke is on me, because medium.com also does not check any SNI / ESNI
field. :)

However, I did notice that my record_digest value was off vs Firefox. And,
now, I don't feel so bad.

I noticed that esni-02 and esni-03 defined record_digest as:

record_digest  A cryptographic hash of the ESNIKeys structure from
      which the ESNI key was obtained, i.e., from the first byte of
      "checksum" to the end of the structure.  This hash is computed
      using the hash function associated with "suite".

but, esni-04 defines record_digest like so:

record_digest  A cryptographic hash of the ESNIKeys structure from
      which the ESNI key was obtained, i.e., from the first byte of
      "version" to the end of the structure.  This hash is computed
      using the hash function associated with "suite".

By following the record_digest algorithm from esni-04, I was able to match
Firefox's record_digest fields for a given domain. This strikes me as very
odd, since the ESNIKeys structure changed in esni-04, but I'm still using
the older, esni-02 definition. So, it sure seems like people are running a
strange mix of drafts at the moment. I think there's still something wrong
with my calculation of the encrypted_sni field--perhaps I will have to read
more NSS source code to figure out what that is. :)

thanks,
Rob

On Sun, Oct 27, 2019 at 2:33 PM Rob Sayre <[email-masked]> wrote:

>> only.esni.defo.ie" ServerHello does show up as TLS 1.3. You check out
>> the details on that host at https://defo.ie/ .
>>
>> you should see some ESNI traffic. Some of the code in the fork is a
>> little messy so far, but it's still in the "make it work" phase. :)
>>
>> thanks,
>> Rob
>>
>>
>>> I initially tested with "cloudflare.com", but found this was a bad
>>> idea, because that host doesn't seem to require an SNI or ESNI. So, a bogus
>>> ESNI triggered no errors.
>>>
>>> I also wondered about extension order. Since the ClientHello.key_share
>>> is part of the ESNI calculation, does it need to appear first in the
>>> extensions list?
>>>
>>> thanks,
>>> Rob
>>>
>>

---

From: Rob Sayre <[email-masked]>
Date: Sun, 27 Oct 2019 21:51:15 -0700

> more NSS source code to figure out what that is. :)

Oh, and sorry for the postscript, but a common piece of cryptographic
mythology is that using the same first bytes for every hash input can
weaken it. I have never looked into that assertion, and I also haven't
looked into whether using a checksum in the way -02 or -03 did could be
problem. Why did the draft originally omit the version number, and why does
it no longer do that?

thanks,
Rob