Draft: draft-ietf-tls-esni

Commentary: This PR does four things : the EncodedClientHelloInner change suggested in . the recommended padding scheme to round the overall length to a multiple of 32 , now that this is fairly easy to compute .  an mistake in the recommended padding scheme . The server sets maximumnamelength to the longest name , but the client computes padding based on the servername extension . We 're miscounting 7 bytes of extension overhead and effectively are using maximumnamelength - 7 instead of maximumname_length . defines what to do if you are n't sending SNI . While unlikely to happen in practice ( no way to get an ECHConfig ) , implementations will need to handle this . The application may well call SetECHConfigList ( ) without SetServerName ( ) and it 's rude to crash . It 's pretty obvious what to do , so just define it . ( If folks would rather the PR only do a subset of these , I can defer the more controversial bits to other PRs . They all touched the same text , so it was easier to just do them all . ) 
 Yeah , I 'd actually already implemented the draft-10 construction , but I prefer this one . As you say , less fuss , especially if you want to do whole - message rounding . I also prefer rounding to a multiple of 32 at the end . If we decrease the range of possible values , it 's easier to GREASE . 
 Oops . Pushed one more fixup to correct the server_name extension overhead . I missed the ServerNameList length prefix , so the correct overhead is 9 , not 7 . See for derivation . 
 one last thing -- would you mind rebasing to resolve conflicts ? 
 Rebased . ( Man , who is this jerk who keeps causing merge conflicts with my PRs ? Let 's see ... oh , it 's That guy is the worst ! :-D ) 
 Given positive support here and on the list , I 'm merging this to include in the next version of the draft . 
 Filing this as a topic for discussion . I think " we do n't like these padding strategies " or " that 's not too complicated " are perfectly plausible answers . So , we need to pad the ECH payload to avoid revealing information via length . It seems to me there are two plausible strategies for padding , and you could do one or both of them . ( You could do both by applying the first , then applying the second . ) NB : I do n't know what the overall consensus is on padding strategies . My assumption has been that there is n't one and we 're building flexibility into protocols so we can figure it out . The below is a summary of my current thinking , but there are probably lots of well - established ideas I do n't know . Pad particular fields you are trying to hide There may be a particular field in your message with a length distribution you would like to hide . Perhaps some anonymity set has server name lengths up to 96 bytes . So you arrange for the client to know this , and the client adds 96 - len(servername ) extra bytes to the ClientHello somewhere . This is easy to reason about in isolation , but only considers individual fields . Information about other fields still leaks . Generically reduce the range of possible lengths You may also just try to generically reduce the range of the function . Maybe you round to a multiple of 32 , or round to the next power of some R 1 . This may still leak information : padding a multiple of 32 still distinguishes ( big server name , big ALPN list ) and ( small server name , small ALPN list ) when the variance is more than 32 bytes . But it pretty uniformly reduces the information leaked in general , which can help increase anonymity sets . For example , an HTTP client will always sends an ALPN of { h2 , http/1.1 } , but a tiny amount of overall padding can help combine HTTP ClientHellos with a non - HTTP client that might have a slightly different ALPN set . It can also mitigate mistakes . For instance , TLS tickets often embed the server name . If the server forgets to pad that when minting the ticket , the first strategy alone will leak this . The recommended padding scheme currently only does the first kind . We add padding for servername and largely ignore the other fields . Suppose some client wanted to additionally use the second strategy . The order of operations here is fussy and interleaves bits of ClientHelloInner and EncodedClientHelloInner : EncodedClientHelloInner has a PSK binder depends on the non - PSK extensions of ClientHelloInner includes the padding extension depends on the length of the non - padding extensions of EncodedClientHelloInner depends on the length of the PSK binder , the non - PSK non - padding extensions of ClientHelloInner , which of them will be compressed This is not actually a circular dependency . But it 's a place the ClientHelloInner and EncodedClientHelloInner are awkward linked together . Probably the way to do it is to maintain a count of how much smaller EncodedClientHelloInner will be and subtract that from the ClientHelloInner as you construct and pad it . If * we think this is a problem , the fix is pretty straightforward : add padding to EncodedClientHelloInner itself . Something like : Now the ClientHelloInner no longer depends on the length of the EncodedClientHelloInner . Is this worth doing ? 
  to mention : Another reason to round the overall payload is that GREASE will likely be much more effective if the space of common lengths is dense . 
 thanks for filing ! This seems reasonable , and probably avoids future one - off padding changes when we discover certain extensions are sensitive to their length . I suppose one could also include the padding extension on ClientHelloInner , but that becomes weird when you consider the ticket / binder example you raise . In general , the current draft text here does seem to be an artifact of the pre - ECH padding story . Since we 're now covering multiple things in the ClientHello , applying padding more broadly seems right . I think it boils down to where we put those bits . ( Your suggestion seems perfectly fine , and should n't be too onerous to implement . It should work fairly nicely with . ) what do you think ? 
 I need to catch up on this discussion , but FWIW , I wrote up some notes on the padding I 've done so far at [ 1 ] . I 'll be fine with changing that later if / as better ideas emerge , so am not that invested in it as of now but it might be useful as an input here . Cheers , S. [ 1 ] 
 +1 , this seems like a good idea to me . 
 Wrote up some text for this
Paragraph (version A):
When sending ClientHello, the client first computes ClientHelloInner,
including any PSK binders.  It then computes a new value, the
EncodedClientHelloInner, by first making a copy of ClientHelloInner.
It then replaces the legacy_session_id field with an empty string.
Paragraph (version B):
The "client_hello" field is computed by first making a copy of
ClientHelloInner and setting the "legacy_session_id" field to the
empty string.  Note this field uses the ClientHello structure,
defined in Section 4.1.2 of RFC8446 which does not include the
Handshake structure's four byte header.  The "zeros" field MUST be
all zeroes.
Paragraph (version A):
Finally, EncodedClientHelloInner is serialized as a ClientHello
structure, defined in Section 4.1.2 of RFC8446.  Note this does not
include the four-byte header included in the Handshake structure.
Paragraph (version B):
Finally, the client pads the message by setting the "zeros" field to
a byte string whose contents are all zeros and whose length is the
amount of padding to add. padding describes a recommended padding
scheme.
Paragraph (version B):
The client-facing server computes ClientHelloInner by reversing this
process.  First it parses EncodedClientHelloInner, interpreting all
bytes after "client_hello" as padding.  If any padding byte is non-
zero, the server MUST abort the connection with an
"illegal_parameter" alert.
Paragraph (version B):
Next it makes a copy of the "client_hello" field and copies the
"legacy_session_id" field from ClientHelloOuter.  It then looks for
an "ech_outer_extensions" extension.  If found, it replaces the
extension with the corresponding sequence of extensions in the
ClientHelloOuter.  If any referenced extensions are missing or if
"encrypted_client_hello" appears in the list, the server MUST abort
the connection with an "illegal_parameter" alert.
Paragraph (version A):
It SHOULD contain TLS padding RFC7685 as described in padding.
Paragraph (version A):
If L >= D, add L - D bytes of padding.  This rounds to the
server's advertised hint, i.e.,
ECHConfig.contents.maximum_name_length.
Paragraph (version A):
Otherwise, let P = 31 - ((D - 1) % 32), and add P bytes of
padding, plus an additional 32 bytes if D + P < L + 32.  This
rounds D up to the nearest multiple of 32 bytes that permits at
least 32 bytes of length ambiguity.
Paragraph (version B):
In contrast, clients do not know the longest SNI value in the client-
facing server's anonymity set without server input.  Clients SHOULD
use the ECHConfig's "maximum_name_length" field as follows, where L
is the "maximum_name_length" value.
Paragraph (version B):
If the ClientHelloInner contained a "server_name" extension with a
name of length D, add max(0, L - D) bytes of padding.
Paragraph (version B):
If the ClientHelloInner did not contain a "server_name" extension
(e.g., if the client is connecting to an IP address), add L + 9
bytes of padding.  This is the length of a "server_name" extension
with an L-byte name.
Paragraph (version B):
Let L be the length of the EncodedClientHelloInner with all the
padding computed so far.
Paragraph (version B):
Let N = 31 - ((L - 1) % 32) and add N bytes of padding.
Paragraph (version B):
This rounds the length of EncodedClientHelloInner up to a multiple of
32 bytes, reducing the set of possible lengths across all clients.
Paragraph (version B):
If these checks succeed, the client-facing server then forwards the
ClientHelloInner to the appropriate backend server, which proceeds as
in backend-server.  If the backend server responds with a
HelloRetryRequest, the client-facing server forwards it, decrypts the
client's second ClientHelloOuter using the procedure in client-
facing-server-hrr, and forwards the resulting second
ClientHelloInner.  The client-facing server forwards all other TLS
messages between the client and backend server unmodified.
Paragraph (version B):
where HKDF-Expand-Label is defined in RFC8446, Section 7.1, "0"
indicates a string of Hash.length bytes set to zero, and Hash is the
hash function used to compute the transcript hash.
Paragraph (version B):
When the backend server sends HelloRetryRequest in response to the
ClientHello, it similarly confirms ECH acceptance by adding a
confirmation signal to its HelloRetryRequest.  But instead of
embedding the signal in the HelloRetryRequest.random (the value of
which is specified by RFC8446), it sends the signal in an extension.
Paragraph (version B):
Section 4.2.2 of RFC8446 defines a cookie value that servers may send
in HelloRetryRequest for clients to echo in the second ClientHello.
While ECH encrypts the cookie in the second ClientHelloInner, the
backend server's HelloRetryRequest is unencrypted.This means
differences in cookies between backend servers, such as lengths or
cleartext components, may leak information about the server identity.
Paragraph (version B):
Backend servers in an anonymity set SHOULD NOT reveal information in
the cookie which identifies the server.  This may be done by handling
HelloRetryRequest statefully, thus not sending cookies, or by using
the same cookie construction for all backend servers.
Paragraph (version B):
Note that, if the cookie includes a key name, analogous to Section 4
of RFC5077, this may leak information if different backend servers
issue cookies with different key names at the time of the connection.
In particular, if the deployment operates in Split Mode, the backend
servers may not share cookie encryption keys.  Backend servers may
mitigate this by either handling key rotation with trial decryption,
or coordinating to match key names.