Draft: draft-ietf-tls-esni

Commentary: I left an OPEN ISSUE about possibly changing the client behavior here depending on how we solve . 
 cc/ 
 cc for a quick look before merging . 
 Paging and for a quick look before merging and cutting -12 . 
 Squashed and rebased . 
 { { client - hrr } } says the client assumes ECH was accepted if the signal matches , but it does n't say what to do if the signal does n't match . Is it an error , or do you ignore it ? Related to , though whether we decide to do that , we need to write down something in the meantime . 
 Seems reasonable to presume rejection , as if the extension were n't present at all , particularly if we end up GREASEing the extension . 
 Right now the server 's not supposed to send it at all . Normally if the peer sends invalid things , we make it an error . But , yeah , allowing it and presuming rejection is plausible when we GREASE it . But that 's not currently in the draft , and I 'm not sure how coherent GREASEing it actually is . Right now , draft-11 as currently published is ambiguous , which makes implementing it hard . 
 I agree we need to say something here . The way I interpreted it in my implementation is : presume ECH acceptance if the ECH extension is present and its payload matches the acceptance signal ; otherwise , presume rejection . I think we should try to spell this behavior in a way that make sense . 
 +1 to assuming rejection , since this is exactly what we do in the non  case . will you be able to prep a PR for this today ? We can include this clarification in -12 . ( Clarifying that clients should abort if the extension contents are malformed , e.g. , of the wrong length , seems fine . But if the signal does n't match , then I think we should just assume rejection , since that would admit greasing the signal . ) 
 Sorry , was OOO yesterday and today was weekend email catchup . I might have time tomorrow ? Alright , sounds like everyone else prefers presuming rejection , so presuming rejection it is . :-) I feel weird doing that in advance of thinking about GREASE , but I guess folks want to do that too , so okay .
Paragraph (version B):
The value of ECHHelloRetryRequest.confirmation is set to
"hrr_accept_confirmation" as described in backend-server-hrr.
Paragraph (version A):
When the server sends a HelloRetryRequest, the client determines if
ECH was accepted by checking the message for an
"encrypted_client_hello" extension with an 8-byte payload equal to
"hrr_accept_confirmation" as defined in backend-server.  If found,
the client presumes acceptance and handles the HelloRetryRequest
using ClientHelloInner.  Otherwise, it presumes rejection and handles
the HelloRetryRequest using ClientHelloOuter.  Note that the client-
facing server does not send "encrypted_client_hello" in case of
rejection.
Paragraph (version B):
When the server sends a HelloRetryRequest, the client checks for the
presence of an "encrypted_client_hello" extension.  If none is found,
then the client presumes rejection and handles the HelloRetryRequest
using ClientHelloOuter.  (Note that the client-facing server does not
send this extension in its HelloRetryRequest.)  Otherwise it proceeds
as follows.
Paragraph (version B):
If the extension's payload has a length other than 8, then the client
aborts the handshake with an "decode_error" alert.  If the payload
length is equal to 8, then the client checks if the payload is equal
to "hrr_accept_confirmation" as defined in backend-server.  If so,
then it presumes acceptance and handles handles the HelloRetryRequest
using ClientHelloInner.  Otherwise, it presumes rejection.
Paragraph (version A):
If "ClientECH.type" is "outer", then the server acts as a client-
facing server and proceeds as described in client-facing-server to
extract a ClientHelloInner, if available.
Paragraph (version A):
If "ClientECH.type" is "inner", then the server acts as a backend
server and proceeds as described in backend-server.
Paragraph (version A):
Otherwise, if "ClientECH.type" is not a valid "ClientECHType",
then the server MUST abort with an "illegal_parameter" alert.
Paragraph (version B):
If "ECHClientHello.type" is "outer", then the server acts as a
client-facing server and proceeds as described in client-facing-
server to extract a ClientHelloInner, if available.
Paragraph (version B):
If "ECHClientHello.type" is "inner", then the server acts as a
backend server and proceeds as described in backend-server.
Paragraph (version B):
Otherwise, if "ECHClientHello.type" is not a valid
"ECHClientHelloType", then the server MUST abort with an
"illegal_parameter" alert.
Paragraph (version A):
Compare ClientECH.config_id against identifiers of each known
ECHConfig and select the ones that match, if any, as candidates.
Paragraph (version A):
Some uses of ECH, such as local discovery mode, may randomize the
ClientECH.config_id since it can be used as a tracking vector.  In
such cases, the second method should be used for matching ClientECH
to known ECHConfig.  See optional-configs.  Unless specified by the
application using (D)TLS or externally configured on both sides,
implementations MUST use the first method.
Paragraph (version A):
The server verifies that the ECHConfig supports the cipher suite
indicated by the ClientECH.cipher_suite and that the version of ECH
indicated by the client matches the ECHConfig.version.  If not, the
server continues to the next candidate ECHConfig.
Paragraph (version B):
Compare ECHClientHello.config_id against identifiers of each known
ECHConfig and select the ones that match, if any, as candidates.
Paragraph (version B):
Some uses of ECH, such as local discovery mode, may randomize the
ECHClientHello.config_id since it can be used as a tracking vector.
In such cases, the second method should be used for matching
ECHClientHello to known ECHConfig.  See optional-configs.  Unless
specified by the application using (D)TLS or externally configured on
both sides, implementations MUST use the first method.
Paragraph (version B):
The server verifies that the ECHConfig supports the cipher suite
indicated by the ECHClientHello.cipher_suite and that the version of
ECH indicated by the client matches the ECHConfig.version.  If not,
the server continues to the next candidate ECHConfig.
Paragraph (version A):
If the client-facing server accepted ECH, it checks the second
ClientHelloOuter also contains the "encrypted_client_hello"
extension.  If not, it MUST abort the handshake with a
"missing_extension" alert.  Otherwise, it checks that
ClientECH.cipher_suite and ClientECH.config_id are unchanged, and
that ClientECH.enc is empty.  If not, it MUST abort the handshake
with an "illegal_parameter" alert.
Paragraph (version B):
If the client-facing server accepted ECH, it checks the second
ClientHelloOuter also contains the "encrypted_client_hello"
extension.  If not, it MUST abort the handshake with a
"missing_extension" alert.  Otherwise, it checks that
ECHClientHello.cipher_suite and ECHClientHello.config_id are
unchanged, and that ECHClientHello.enc is empty.  If not, it MUST
abort the handshake with an "illegal_parameter" alert.
Paragraph (version A):
If the client-facing server rejected ECH, or if the first ClientHello
did not include an "encrypted_client_hello" extension, the client-
facing server proceeds with the connection as usual.  The server does
not decrypt the second ClientHello's ClientECH.payload value, if
there is one.
Paragraph (version B):
If the client-facing server rejected ECH, or if the first ClientHello
did not include an "encrypted_client_hello" extension, the client-
facing server proceeds with the connection as usual.  The server does
not decrypt the second ClientHello's ECHClientHello.payload value, if
there is one.
Paragraph (version A):
Client-facing server configuration determines the size of the
anonymity set.  For example, if a client-facing server uses distinct
ECHConfig values for each host, then each anonymity set has size k =
maximize the size of the anonymity set where possible.  This means
client-facing servers should use the same ECHConfig for as many hosts
as possible.  An attacker can distinguish two hosts that have
different ECHConfig values based on the ClientECH.config_id value.
This also means public information in a TLS handshake is also
consistent across hosts.  For example, if a client-facing server
services many backend origin hosts, only one of which supports some
cipher suite, it may be possible to identify that host based on the
contents of unencrypted handshake messages.
Paragraph (version B):
Client-facing server configuration determines the size of the
anonymity set.  For example, if a client-facing server uses distinct
ECHConfig values for each host, then each anonymity set has size k =
maximize the size of the anonymity set where possible.  This means
client-facing servers should use the same ECHConfig for as many hosts
as possible.  An attacker can distinguish two hosts that have
different ECHConfig values based on the ECHClientHello.config_id
value.  This also means public information in a TLS handshake is also
consistent across hosts.  For example, if a client-facing server
services many backend origin hosts, only one of which supports some
cipher suite, it may be possible to identify that host based on the
contents of unencrypted handshake messages.
Paragraph (version A):
Optional configuration identifiers may be useful in scenarios where
clients and client-facing servers do not want to reveal information
about the client-facing server in the "encrypted_client_hello"
extension.  In such settings, clients send a randomly generated
config_id in the ClientECH.  Servers in these settings must perform
trial decryption since they cannot identify the client's chosen ECH
key using the config_id value.  As a result, support for optional
configuration identifiers may exacerbate DoS attacks.  Specifically,
an adversary may send malicious ClientHello messages, i.e., those
which will not decrypt with any known ECH key, in order to force
wasteful decryption.  Servers that support this feature should, for
example, implement some form of rate limiting mechanism to limit the
damage caused by such attacks.
Paragraph (version B):
Optional configuration identifiers may be useful in scenarios where
clients and client-facing servers do not want to reveal information
about the client-facing server in the "encrypted_client_hello"
extension.  In such settings, clients send a randomly generated
config_id in the ECHClientHello.  Servers in these settings must
perform trial decryption since they cannot identify the client's
chosen ECH key using the config_id value.  As a result, support for
optional configuration identifiers may exacerbate DoS attacks.
Specifically, an adversary may send malicious ClientHello messages,
i.e., those which will not decrypt with any known ECH key, in order
to force wasteful decryption.  Servers that support this feature
should, for example, implement some form of rate limiting mechanism
to limit the damage caused by such attacks.
Paragraph (version A):
This design requires servers to decrypt ClientHello messages with
ClientECH extensions carrying valid digests.  Thus, it is possible
for an attacker to force decryption operations on the server.  This
attack is bound by the number of valid TCP connections an attacker
can open.
Paragraph (version B):
This design requires servers to decrypt ClientHello messages with
ECHClientHello extensions carrying valid digests.  Thus, it is
possible for an attacker to force decryption operations on the
server.  This attack is bound by the number of valid TCP connections
an attacker can open.