Change:
3.2. <del> SNI encryption requires that each provider publish a public key and metadata which is used for SNI encryption for all the domains for which it serves directly or indirectly (via Split Mode).  This document defines the format of the SNI encryption public key and metadata, referred to as an ESNI configuration, and delegates DNS publication details to HTTPSSVC, though other delivery mechanisms are possible.  In particular, if some of the clients of a private server are applications rather than Web browsers, those applications might have the public key and metadata preconfigured. </del> <ins> ECHO works by encrypting the entire ClientHello, including the SNI and any additional extensions such as ALPN.  This requires that each provider publish a public key and metadata which is used for ClientHello encryption for all the domains for which it serves directly or indirectly (via Split Mode).  This document defines the format of the SNI encryption public key and metadata, referred to as an ECHO configuration, and delegates DNS publication details to HTTPSSVC, though other delivery mechanisms are possible.  In particular, if some of the clients of a private server are applications rather than Web browsers, those applications might have the public key and metadata preconfigured. </ins> When a client wants to form a TLS connection to any of the domains <del> served by an ESNI-supporting provider, it sends an "encrypted_server_name" extension, which contains the true extension encrypted under the provider's public key.  The provider can then decrypt the extension and either terminate the connection (in Shared Mode) or forward it to the backend server (in Split Mode). </del> <ins> served by an ECHO-supporting provider, it constructs a ClientHello in the regular fashion containing the true SNI value (ClientHelloInner) and then encrypts it using the public key for the provider.  It then constructs a new ClientHello (ClientHelloOuter) with an innocuous SNI (and potentially innocuous versions of other extensions such as ALPN RFC7301) and containing the encrypted ClientHelloInner as an extension.  It sends ClientHelloOuter to the server. Upon receiving ClientHelloOuter, the server can then decrypt ClientHelloInner and either terminate the connection (in Shared Mode) or forward it to the backend server (in Split Mode). Note that both ClientHelloInner and ClientHelloOuter are both valid, complete ClientHello messages.  ClientHelloOuter carries an encrypted representation of ClientHelloInner in a "encrypted_client_hello" extension, defined in encrypted-client-hello. </ins> 4. <del> SNI Encryption configuration information is conveyed with the following ESNIConfig structure. </del> <ins> ClientHello encryption configuration information is conveyed with the following ECHOConfigs structure. </ins> <del> The ESNIConfig structure contains the following fields: </del> <ins> The ECHOConfigs structure contains one or more ECHOConfig structures in decreasing order of preference.  This allows a server to support multiple versions of ECHO and multiple sets of ECHO parameters. The ECHOConfig structure contains the following fields: </ins> The version of the structure.  For this specification, that value <del> SHALL be 0xff03.  Clients MUST ignore any ESNIConfig structure with a version they do not understand.  [[NOTE: This means that the RFC will presumably have a nonzero value.]] </del> <ins> SHALL be 0xff03.  Clients MUST ignore any ECHOConfig structure with a version they do not understand. An opaque byte string whose contents depend on the version of the structure.  For this specification, the contents are an ECHOConfigContents structure. The ECHOConfigContents structure contains the following fields: </ins> The non-empty name of the entity trusted to update these encryption keys.  This is used to repair misconfigurations, as described in handle-server-response. <del> The list of keys which can be used by the client to encrypt the SNI.  Every key being listed MUST belong to a different group. </del> <ins> The HPKE I-D.irtf-cfrg-hpke public key which can be used by the client to encrypt the ClientHello. The HPKE I-D.irtf-cfrg-hpke KEM identifier corresponding to public_key.  Clients MUST ignore any ECHOConfig structure with a key using a KEM they do not support. The list of TLS cipher suites which can be used by the client to encrypt the ClientHello.  See hpke-map for information on how a cipher suite maps to corresponding HPKE algorithm identifiers. </ins> <del> padded_length The length to pad the ServerNameList value to prior to encryption.  This value SHOULD be set to the largest ServerNameList the server expects to support rounded up the nearest multiple of 16. If the server supports arbitrary wildcard names, it SHOULD set this value to 260.  Clients SHOULD reject ESNIConfig as invalid if padded_length is greater than 260. </del> <ins> the largest name the server expects to support.  If the server supports arbitrary wildcard names, it SHOULD set this value to 256.  Clients SHOULD reject ESNIConfig as invalid if maximum_name_length is greater than 256. </ins> A list of extensions that the client can take into consideration <del> when generating a Client Hello message.  The format is defined in RFC8446; Section 4.2.  The purpose of the field is to provide room for additional features in the future.  An extension may be tagged as mandatory by using an extension type codepoint with the high order bit set to 1.  A client which receives a mandatory extension they do not understand must reject the ESNIConfig content. Any of the listed keys in the ESNIConfig value may be used to encrypt the SNI for the associated domain name.  The cipher suite list is orthogonal to the list of keys, so each key may be used with any cipher suite.  Clients MUST parse the extension list and check for unsupported mandatory extensions.  If an unsupported mandatory extension is present, clients MUST reject the ESNIConfig value. </del> <ins> when generating a Client Hello message.  The purpose of the field is to provide room for additional features in the future.  The format is defined in RFC8446; Section 4.2.  The same interpretation rules apply: extensions MAY appear in any order, but there MUST NOT be more than one extension of the same type in the extensions block.  An extension may be tagged as mandatory by using an extension type codepoint with the high order bit set to 1.  A client which receives a mandatory extension they do not understand must reject the ECHOConfig content. Clients MUST parse the extension list and check for unsupported mandatory extensions.  If an unsupported mandatory extension is present, clients MUST reject the ECHOConfig value. </ins> 5. <del> The encrypted SNI is carried in an "encrypted_server_name" extension, defined as follows: </del> <ins> The encrypted ClientHelloInner is carried in an "encrypted_client_hello" extension, defined as follows: </ins> For clients (in ClientHello), this extension contains the following <del> ClientEncryptedSNI structure: </del> <ins> ClientEncryptedCH structure: </ins> <del> The cipher suite used to encrypt the SNI. </del> <ins> The cipher suite used to encrypt ClientHelloInner. </ins> <del> The KeyShareEntry carrying the client's public ephemeral key share used to derive the ESNI key. A cryptographic hash of the ESNIConfig structure from which the ESNI key was obtained, i.e., from the first byte of "version" to </del> <ins> A cryptographic hash of the ECHOConfig structure from which the ECHO key was obtained, i.e., from the first byte of "version" to </ins> the end of the structure.  This hash is computed using the hash function associated with "suite". <del> The ClientESNIInner structure, AEAD-encrypted using cipher suite "suite" and the key generated as described below. For servers (in EncryptedExtensions), this extension contains the following structure: </del> <ins> The HPKE encapsulated key, used by servers to decrypt the corresponding encrypted_ch field. </ins> <del> Indicates whether the server processed the client ESNI extension. (See handle-server-response and server-behavior.} </del> <ins> The serialized and encrypted ClientHelloInner structure, AEAD- encrypted using cipher suite "suite" and the key generated as described below. </ins> <del> The contents of ClientESNIInner.nonce.  (See client-behavior.) </del> <ins> If the server accepts ECHO, it does not send this extension.  If it rejects ECHO, then it sends the following structure in EncryptedExtensions: </ins> <del> One or more ESNIConfig structures containing the keys that the client should use on subsequent connections to encrypt the ClientESNIInner structure. </del> <ins> An ESNIConfigs structure containing one or more ECHOConfig structures in decreasing order of preference that the client should use on subsequent connections to encrypt the ClientHelloInner structure. </ins> <del> This protocol also defines the "esni_required" alert, which is sent </del> <ins> This protocol also defines the "echo_required" alert, which is sent </ins> by the client when it offered an "encrypted_server_name" extension which was not accepted by the server. <ins> 6. When using ECHO, the client MUST also add an extension of type "echo_nonce" to the ClientHelloInner (but not to the outer ClientHello).  This nonce ensures that the server's encrypted Certificate can only be read by the entity which sent this ClientHello.  [[TODO: Describe HRR cut-and-paste 1 in Security Considerations.]] This extension is defined as follows: A 16-byte nonce exported from the HPKE encryption context.  See send-echo for details about its computation. </ins> Finally, requirements in client-behavior and server-behavior require implementations to track, alongside each PSK established by a previous connection, whether the connection negotiated this extension <del> with the "esni_accept" response type.  If so, this is referred to as an "ESNI PSK".  Otherwise, it is a "non-ESNI PSK".  This may be </del> <ins> with the "echo_accept" response type.  If so, this is referred to as an "ECHO PSK".  Otherwise, it is a "non-ECHO PSK".  This may be </ins> implemented by adding a new field to client and server session states. <del> 5.1. 5.1.1. In order to send an encrypted SNI, the client MUST first select one of the server ESNIKeyShareEntry values and generate an (EC)DHE share in the matching group.  This share will then be sent to the server in the "encrypted_server_name" extension and used to derive the SNI encryption key.  It does not affect the (EC)DHE shared secret used in the TLS key schedule.  The client MUST also select an appropriate cipher suite from the list of suites offered by the server.  If the client is unable to select an appropriate group or suite it SHOULD ignore that ESNIConfig value and MAY attempt to use another value provided by the server.  The client MUST NOT send encrypted SNI using groups or cipher suites not advertised by the server. When offering an encrypted SNI, the client MUST NOT offer to resume any non-ESNI PSKs.  It additionally MUST NOT offer to resume any sessions for TLS 1.2 or below. Let Z be the DH shared secret derived from a key share in ESNIConfig and the corresponding client share in ClientEncryptedSNI.key_share. The SNI encryption key is computed from Z as follows: where ESNIContents is as specified below and Hash is the hash function associated with the HKDF instantiation.  The salt argument for HKDF-Extract is a string consisting of Hash.length bytes set to zeros.  For a client's first ClientHello, KeyLabel = "esni key" and IVLabel = "esni iv", whereas for a client's second ClientHello, sent in response to a HelloRetryRequest, KeyLabel = "hrr esni key" and IVLabel = "hrr esni iv".  (This label variance is done to prevent nonce re-use since the client's ESNI key share, and thus the value of Zx, does not change across ClientHello retries.) Note that ESNIContents will not be directly transmitted to the server in the ClientHello.  The server will instead reconstruct the same object by obtaining its values from ClientEncryptedSNI and ClientHello. [[TODO: label swapping fixes a bug in the spec, though this may not be the best way to deal with HRR.  See https://github.com/tlswg/ draft-ietf-tls-esni/issues/121 and https://github.com/tlswg/draft- ietf-tls-esni/pull/170 for more details.]] Same value as ClientEncryptedSNI.record_digest. Same value as ClientEncryptedSNI.key_share. Same nonce as ClientHello.random. The client then creates a ClientESNIInner structure: A random 16-octet value to be echoed by the server in the "encrypted_server_name" extension. The true SNI DNS name, that is, the HostName value that would have been sent in the plaintext "server_name" extension.  (NameType values other than "host_name" are unsupported since SNI extensibility failed SNIExtensibilityFailed). Zero padding whose length makes the serialized PaddedServerNameList struct have a length equal to ESNIConfig.padded_length. This value consists of the serialized ServerNameList from the "server_name" extension, padded with enough zeroes to make the total structure ESNIConfig.padded_length bytes long.  The purpose of the padding is to prevent attackers from using the length of the "encrypted_server_name" extension to determine the true SNI.  If the serialized ServerNameList is longer than ESNIConfig.padded_length, the client MUST NOT use the "encrypted_server_name" extension. The ClientEncryptedSNI.encrypted_sni value is then computed using the usual TLS 1.3 AEAD: Where KeyShareClientHello is the "extension_data" field of the "key_share" extension in a Client Hello (Section 4.2.8 of RFC8446)). Including KeyShareClientHello in the AAD of AEAD-Encrypt binds the ClientEncryptedSNI value to the ClientHello and prevents cut-and- paste attacks. Note: future extensions may end up reusing the server's ESNIKeyShareEntry for other purposes within the same message (e.g., encrypting other values).  Those usages MUST have their own HKDF labels to avoid reuse. [[OPEN ISSUE: If in the future you were to reuse these keys for 0-RTT priming, then you would have to worry about potentially expanding twice of Z_extracted.  We should think about how to harmonize these to make sure that we maintain key separation.]] This value is placed in an "encrypted_server_name" extension. The client MUST place the value of ESNIConfig.public_name in the "server_name" extension.  (This is required for technical conformance with RFC7540; Section 9.2.)  The client MUST NOT send a "cached_info" extension RFC7924 with a CachedObject entry whose </del> <ins> 6.1. Some TLS 1.3 extensions can be quite large and having them both in the inner and outer ClientHello will lead to a very large overall size.  One particularly pathological example is "key_share" with post-quantum algorithms.  In order to reduce the impact of duplicated extensions, the client may use the "outer_extensions" extension. OuterExtensions MUST only be used in ClientHelloInner.  It consists of one or more ExtensionType values, each of which reference an extension in ClientHelloOuter, and a digest of the complete ClientHelloInner. When sending ClientHello, the client first computes ClientHelloInner, including any PSK binders, and then MAY substitute extensions which it knows will be duplicated in ClientHelloOuter.  To do so, the client computes a hash H of the entire ClientHelloInner message with the same hash as for the KDF used to encrypt ClienHelloInner.  Then, the client removes and and replaces extensions from ClientHelloInner with a single "outer_extensions" extension.  The list of outer_extensions include those which were removed from ClientHelloInner, in the order in which they were removed.  The hash contains the full ClientHelloInner hash H computed above. This process is reversed by client-facing servers upon receipt. Specifically, the server replaces the "outer_extensions" with extensions contained in ClientHelloOuter.  The server then computes a hash H' of the reconstructed ClientHelloInner.  If H' does not equal OuterExtensions.hash, the server aborts the connection with an "illegal_parameter" alert. Clients SHOULD only use this mechanism for extensions which are large.  All other extensions SHOULD appear in both ClientHelloInner and ClientHelloOuter even if they have identical values. 7. 7.1. In order to send an encrypted ClientHello, the client first determines if it supports the server's chosen KEM, as identified by ECHOConfig.kem_id.  If one is supported, the client MUST select an appropriate cipher suite from the list of suites offered by the server.  If the client does not support the corresponding KEM or is unable to select an appropriate group or suite, it SHOULD ignore that ECHOConfig value and MAY attempt to use another value provided by the server.  The client MUST NOT send ECHO using HPKE algorithms not advertised by the server. Given a compatible ECHOConfig with fields public_key and kem_id, carrying the HpkePublicKey and KEM identifier corresponding to the server, clients compute an HPKE encryption context as follows: Note that the HPKE algorithm identifiers are those which match the client's chosen CipherSuite, according to hpke-map.  The client MAY replace any large, duplicated extensions in ClientHelloInner with the corresponding "outer_extensions" extension, as described in outer- extensions. The client then generates a ClientHelloInner value.  In addition to the normal values, ClientHelloInner MUST also contain: an "echo_nonce" extension TLS padding Padding SHOULD be P = L - D bytes, where L = ECHOConfig.maximum_name_length, rounded up to the nearest multiple of 16 D = len(dns_name), where dns_name is the DNS name in the ClientHelloInner "server_name" extension When offering an encrypted ClientHello, the client MUST NOT offer to resume any non-ECHO PSKs.  It additionally MUST NOT offer to resume any sessions for TLS 1.2 or below. The encrypted ClientHello value is then computed as: Finally, the client MUST generate a ClientHelloOuter message containing the "encrypted_client_hello" extension with the values as indicated above.  In particular, suite contains the client's chosen ciphersuite; record_digest contains the digest of the corresponding ECHOConfig structure; enc contains the encapsulated key as output by SetupBaseS; and encrypted_ch contains the HPKE encapsulated key (enc) and the ClientHelloInner ciphertext (encrypted_ch_inner). The client MUST place the value of ECHOConfig.public_name in the ClientHelloOuter "server_name" extension.  The remaining contents of the ClientHelloOuter MAY be identical to those in ClientHelloInner but MAY also differ.  The ClientHelloOuter MUST NOT contain a "cached_info" extension RFC7924 with a CachedObject entry whose </ins> CachedInformationType is "cert", since this indication would divulge the true server name. <del> 5.1.2. If the server negotiates TLS 1.3 or above and provides an "encrypted_server_name" extension in EncryptedExtensions, the client then processes the extension's "response_type" field: If the value is "esni_accept", the client MUST check that the extension's "nonce" field matches ClientESNIInner.nonce and otherwise abort the connection with an "illegal_parameter" alert. The client then proceeds with the connection as usual, authenticating the connection for the origin server. If the value is "esni_retry_request", the client proceeds with the handshake, authenticating for ESNIConfig.public_name as described in auth-public-name.  If authentication or the handshake fails, the client MUST return a failure to the calling application.  It MUST NOT use the retry keys. Otherwise, when the handshake completes successfully with the public name authenticated, the client MUST abort the connection with an "esni_required" alert.  It then processes the "retry_keys" field from the server's "encrypted_server_name" extension. If one of the values contains a version supported by the client, it can regard the ESNI keys as securely replaced by the server. It SHOULD retry the handshake with a new transport connection, using that value to encrypt the SNI.  The value may only be applied to the retry connection.  The client MUST continue to use the previously-advertised keys for subsequent connections.  This avoids introducing pinning concerns or a tracking vector, should a malicious server present client-specific retry keys to identify clients. If none of the values provided in "retry_keys" contains a supported version, the client can regard ESNI as securely disabled by the server.  As below, it SHOULD then retry the handshake with a new transport connection and ESNI disabled. If the field contains any other value, the client MUST abort the connection with an "illegal_parameter" alert. </del> <ins> 7.2. As described in server-behavior, the server MAY either accept ECHO and use ClientHelloInner or reject it and use ClientHelloOuter. However, there is no indication in ServerHello of which one the server has done and the client must therefore use trial decryption in order to determine this. 7.2.1. If the server used ClientHelloInner, the client proceeds with the connection as usual, authenticating the connection for the origin server. 7.2.2. If the server used ClientHelloOuter, the client proceeds with the handshake, authenticating for ECHOConfig.public_name as described in auth-public-name.  If authentication or the handshake fails, the client MUST return a failure to the calling application.  It MUST NOT use the retry keys. Otherwise, when the handshake completes successfully with the public name authenticated, the client MUST abort the connection with an "echo_required" alert.  It then processes the "retry_keys" field from the server's "encrypted_server_name" extension. If one of the values contains a version supported by the client, it can regard the ECHO keys as securely replaced by the server.  It SHOULD retry the handshake with a new transport connection, using that value to encrypt the ClientHello.  The value may only be applied to the retry connection.  The client MUST continue to use the previously-advertised keys for subsequent connections.  This avoids introducing pinning concerns or a tracking vector, should a malicious server present client-specific retry keys to identify clients. If none of the values provided in "retry_keys" contains a supported version, the client can regard ECHO as securely disabled by the server.  As below, it SHOULD then retry the handshake with a new transport connection and ECHO disabled. If the field contains any other value, the client MUST abort the connection with an "illegal_parameter" alert. </ins> If the server negotiates an earlier version of TLS, or if it does not provide an "encrypted_server_name" extension in EncryptedExtensions, the client proceeds with the handshake, authenticating for <del> ESNIConfig.public_name as described in auth-public-name.  If an earlier version was negotiated, the client MUST NOT enable the False Start optimization RFC7918 for this handshake.  If authentication or the handshake fails, the client MUST return a failure to the calling application.  It MUST NOT treat this as a secure signal to disable ESNI. </del> <ins> ECHOConfigContents.public_name as described in auth-public-name.  If an earlier version was negotiated, the client MUST NOT enable the False Start optimization RFC7918 for this handshake.  If authentication or the handshake fails, the client MUST return a failure to the calling application.  It MUST NOT treat this as a secure signal to disable ECHO. </ins> Otherwise, when the handshake completes successfully with the public name authenticated, the client MUST abort the connection with an <del> "esni_required" alert.  The client can then regard ESNI as securely </del> <ins> "echo_required" alert.  The client can then regard ECHO as securely </ins> disabled by the server.  It SHOULD retry the handshake with a new <del> transport connection and ESNI disabled. </del> <ins> transport connection and ECHO disabled. </ins> [[TODO: Key replacement is significantly less scary than saying that <del> ESNI-naive servers bounce ESNI off.  Is it worth defining a strict mode toggle in the ESNI keys, for a deployment to indicate it is </del> <ins> ECHO-naive servers bounce ECHO off.  Is it worth defining a strict mode toggle in the ECHO keys, for a deployment to indicate it is </ins> ready for that? ]] Clients SHOULD implement a limit on retries caused by <del> "esni_retry_request" or servers which do not acknowledge the </del> <ins> "echo_retry_request" or servers which do not acknowledge the </ins> "encrypted_server_name" extension.  If the client does not retry in either scenario, it MUST report an error to the calling application. <del> If the server sends a HelloRetryRequest in response to the ClientHello and the client can send a second updated ClientHello per the rules in RFC8446, the "encrypted_server_name" extension values which do not depend on the (possibly updated) KeyShareClientHello, i.e,, ClientEncryptedSNI.suite, ClientEncryptedSNI.key_share, and ClientEncryptedSNI.record_digest, MUST NOT change across ClientHello messages.  Moreover, ClientESNIInner MUST not change across ClientHello messages.  Informally, the values of all unencrypted extension information, as well as the inner extension plaintext, must be consistent between the first and second ClientHello messages. 5.1.3. </del> <ins> 7.2.2.1. </ins> When the server cannot decrypt or does not process the "encrypted_server_name" extension, it continues with the handshake using the cleartext "server_name" extension instead (see server- <del> behavior).  Clients that offer ESNI then authenticate the connection </del> <ins> behavior).  Clients that offer ECHO then authenticate the connection </ins> with the public name, as follows: If the server resumed a session or negotiated a session that did not use a certificate for authentication, the client MUST abort the connection with an "illegal_parameter" alert.  This case is <del> invalid because send-esni requires the client to only offer ESNI- </del> <ins> invalid because send-echo requires the client to only offer ECHO- </ins> established sessions, and server-behavior requires the server to <del> decline ESNI-established sessions if it did not accept ESNI. </del> <ins> decline ECHO-established sessions if it did not accept ECHO. </ins> The client MUST verify that the certificate is valid for <del> ESNIConfig.public_name.  If invalid, it MUST abort the connection with the appropriate alert. </del> <ins> ECHOConfigContents.public_name.  If invalid, it MUST abort the connection with the appropriate alert. </ins> If the server requests a client certificate, the client MUST respond with an empty Certificate message, denoting no client

Discussion:
This change implements the ESNI -> ECHO design discussed in Singapore. There a couple pending TODOs, particularly around cross-CH HRR binding, but the meat is what's currently being analyzed. cc NAME NAME NAME NAME
This still seems a bit rough, but I think better in the spec than out.