10.7.  To signal acceptance, the backend server overwrites 8 bytes of its ServerHello.random with a value derived from the ClientHelloInner.random.  (See backend-server for details.)  This Section 4.1.3).  These mechanisms do not interfere because the backend server only signals ECH acceptance in TLS 1.3 or higher. <del> 10.8. </del>  RFC8744 lists several requirements for SNI encryption.  In this section, we re-iterate these requirements and assess the ECH design against them. <del> 10.8.1. </del>  Since servers process either ClientHelloInner or ClientHelloOuter, and because ClientHelloInner.random is encrypted, it is not possible for an attacker to "cut and paste" the ECH value in a different Client Hello and learn information from ClientHelloInner. <del> 10.8.2. </del>  This design depends upon DNS as a vehicle for semi-static public key distribution.  Server operators may partition their private keys by publishing different DNS records containing ECHConfig values with different keys using a short TTL. <del> 10.8.3. </del>  This design requires servers to decrypt ClientHello messages with ClientECH extensions carrying valid digests.  Thus, it is possible attack is bound by the number of valid TCP connections an attacker can open. <del> 10.8.4. </del>  As a means of reducing the impact of network ossification, RFC8744 recommends SNI-protection mechanisms be designed in such a way that some mitigations require coordination between the client and server. These mitigations are out-of-scope for this specification. <del> 10.8.5. </del>  This design is not forward secret because the server's ECH key is static.  However, the window of exposure is bound by the key lifetime.  It is RECOMMENDED that servers rotate keys frequently. <del> 10.8.6. </del>  This design permits servers operating in Split Mode to forward connections directly to backend origin servers.  The client the client-facing server because the client validates the server certificate against the public name before retrying. <del> 10.8.7. </del>  This design has no impact on application layer protocol negotiation. It may affect connection routing, server certificate selection, and entire ClientHello, this design additionally supports encrypting the ALPN extension. <del> 10.9. </del>  Variations in the length of the ClientHelloInner ciphertext could leak information about the corresponding plaintext. padding describes a RECOMMENDED padding mechanism for clients aimed at reducing potential information leakage. <del> 10.10. </del>  This section describes the rationale for ECH properties and mechanics as defenses against active attacks.  In all the attacks below, the of the attacker is to learn private information about the inner ClientHello, such as the true SNI value. <del> 10.10.1. </del>  This attack uses the client's reaction to an incorrect certificate as an oracle.  The attacker intercepts a legitimate ClientHello and Certificate message.  Thus, the client will fail to decrypt the Certificate and abort the connection. <del> 10.10.2. </del>  This attack aims to exploit server HRR state management to recover information about a legitimate ClientHello using its own attacker- for equality and changes behavior based on the result can be used as an oracle to learn the client's SNI. <del> 10.10.3. </del>  This attack aims to leak information about secret parts of the encrypted ClientHello by adding attacker-controlled parameters and

One of the motivations for encrypting HRR in was the leaks from cookie values. If we don't go with such a construction (which seems likely) and instead do or , cookies will be in the clear. That means it is instead on the backend servers to construct cookie values without leaking information. This PR provides security considerations to that effect. Unfortunately the requirements on backend servers within a Split Mode deployment are a bit onerous. I suspect, in practice, the answer is that Split Mode deployments cannot even use cookies on the backend server. Between HRRs being rare and stateless HRR being unnecessary over TCP or QUIC, that's hopefully fine. (Note this is different from , which was about the client-facing server being stateless. This applies if the backend server is stateless.)

10.7. <ins> 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. 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. 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. 10.8. </ins> To signal acceptance, the backend server overwrites 8 bytes of its ServerHello.random with a value derived from the ClientHelloInner.random.  (See backend-server for details.)  This Section 4.1.3).  These mechanisms do not interfere because the backend server only signals ECH acceptance in TLS 1.3 or higher.  <ins> 10.9. </ins> RFC8744 lists several requirements for SNI encryption.  In this section, we re-iterate these requirements and assess the ECH design against them.  <ins> 10.9.1. </ins> Since servers process either ClientHelloInner or ClientHelloOuter, and because ClientHelloInner.random is encrypted, it is not possible for an attacker to "cut and paste" the ECH value in a different Client Hello and learn information from ClientHelloInner.  <ins> 10.9.2. </ins> This design depends upon DNS as a vehicle for semi-static public key distribution.  Server operators may partition their private keys by publishing different DNS records containing ECHConfig values with different keys using a short TTL.  <ins> 10.9.3. </ins> This design requires servers to decrypt ClientHello messages with ClientECH extensions carrying valid digests.  Thus, it is possible attack is bound by the number of valid TCP connections an attacker can open.  <ins> 10.9.4. </ins> As a means of reducing the impact of network ossification, RFC8744 recommends SNI-protection mechanisms be designed in such a way that some mitigations require coordination between the client and server. These mitigations are out-of-scope for this specification.  <ins> 10.9.5. </ins> This design is not forward secret because the server's ECH key is static.  However, the window of exposure is bound by the key lifetime.  It is RECOMMENDED that servers rotate keys frequently.  <ins> 10.9.6. </ins> This design permits servers operating in Split Mode to forward connections directly to backend origin servers.  The client the client-facing server because the client validates the server certificate against the public name before retrying.  <ins> 10.9.7. </ins> This design has no impact on application layer protocol negotiation. It may affect connection routing, server certificate selection, and entire ClientHello, this design additionally supports encrypting the ALPN extension.  <ins> 10.10. </ins> Variations in the length of the ClientHelloInner ciphertext could leak information about the corresponding plaintext. padding describes a RECOMMENDED padding mechanism for clients aimed at reducing potential information leakage.  <ins> 10.11. </ins> This section describes the rationale for ECH properties and mechanics as defenses against active attacks.  In all the attacks below, the of the attacker is to learn private information about the inner ClientHello, such as the true SNI value.  <ins> 10.11.1. </ins> This attack uses the client's reaction to an incorrect certificate as an oracle.  The attacker intercepts a legitimate ClientHello and Certificate message.  Thus, the client will fail to decrypt the Certificate and abort the connection.  <ins> 10.11.2. </ins> This attack aims to exploit server HRR state management to recover information about a legitimate ClientHello using its own attacker- for equality and changes behavior based on the result can be used as an oracle to learn the client's SNI.  <ins> 10.11.3. </ins> This attack aims to leak information about secret parts of the encrypted ClientHello by adding attacker-controlled parameters and


the query domain name.  That is, if a client queries example.com, the ESNI TXT Resource Record might be: <del> Servers MUST ensure that, if multiple A, AAAA or ALTSVC I-D.schwartz- httpbis-dns-alt-svc records are returned for a domain with ESNI support, all the servers pointed to by those records are able to handle the keys returned as part of a ESNI TXT record for that domain. </del>  Alt-Svc records may be used to inform the client of the plaintext (fronting) SNI.  If present, clients SHOULD use its value in the SNI

Also see URL
LGTM -- thanks!
I suggest dropping the references to Ben's individual alt-svc in DNS draft.. it hasn't been adopted and its future is murky. actual alt-svc (the non dns kind) actually requires the use of the plaintext SNI. As those are both cachable and connections to new IP addresses (less linkable) you could consider updating it with mike bishop's URL (also an individual draft atm)
NAME also suggested that the reference might not be needed in URL so I made to actually remove it.

the query domain name.  That is, if a client queries example.com, the ESNI TXT Resource Record might be:  <ins> Servers MUST ensure that, if multiple A or AAAA records are returned for a domain with ESNI support, all the servers pointed to by those records are able to handle the keys returned as part of a ESNI TXT record for that domain. </ins> Alt-Svc records may be used to inform the client of the plaintext (fronting) SNI.  If present, clients SHOULD use its value in the SNI


"ECHConfigContents.cipher_suites" list. The HPKE encapsulated key, used by servers to decrypt the <del> corresponding "payload" field. </del>  The serialized and encrypted ClientHelloInner structure, encrypted using HPKE as described in real-ech.

The second ClientHello after HelloRetryRequest reuses the HPKE context and calls Seal/Open a second time. To avoid needing to unnecessarily retain the enc value, and discourage servers from mistakenly recreating the HPKE context, we made it use the empty string but forgot to update the syntax to match.

"ECHConfigContents.cipher_suites" list. The HPKE encapsulated key, used by servers to decrypt the  <ins> corresponding "payload" field.  This field is empty in ClientHelloOuters sent in response to HelloRetryRequest. </ins> The serialized and encrypted ClientHelloInner structure, encrypted using HPKE as described in real-ech.


that of any valid ECHConfig, which could include any ECHConfig that has been recently removed from active use. <del> The longest name of a backend server, if known.  If this value is not known it can be set to zero, in which case clients SHOULD use the inner ClientHello padding scheme described below.  That could happen if wildcard names are in use, or if names can be added or removed from the anonymity set during the lifetime of a particular ECH configuration. </del>  The non-empty name of the client-facing server, i.e., the entity trusted to update the ECH configuration.  This is used to correct

While padding is "described below", it's pretty far away now. We also recommend the padding scheme whether or not the value is non-zero. The text about wildcard names also applies in both cases and seems to describe when the server name may exceed the max. This rephases things a bit.

that of any valid ECHConfig, which could include any ECHConfig that has been recently removed from active use.  <ins> The longest name of a backend server, if known.  If not known, this value can be set to zero.  It is used to compute padding (padding) and does not constrain server name lengths.  Names may exceed this length if, e.g., the server uses wildcard names or added new names to the anonymity set. </ins> The non-empty name of the client-facing server, i.e., the entity trusted to update the ECH configuration.  This is used to correct


the client may use the "ech_outer_extensions" extension. OuterExtensions consists of one or more ExtensionType values, each of <del> which reference an extension in ClientHelloOuter. </del>  When sending ClientHello, the client first computes ClientHelloInner, including any PSK binders.  It then computes a new value, the 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 <del> 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. </del>  <del> The "ech_outer_extensions" extension is only used for compressing the ClientHelloInner.  It MUST NOT be sent in either ClientHelloOuter or ClientHelloInner. </del>  <del> Note that it is possible to implement decoding of the EncodedClientHelloInner in a way that creates a denial-of-service vulnerability.  Specifically, the server needs to check that each extension in the OuterExtensions list appears in the ClientHelloOuter.  The naive strategy would require O(N*M) time, where N is the number of extensions in the ClientHelloOuter and M is the number of extensions in the OuterExtensions list.  Malicious clients could exploit this behavior in order to cause excessive work for the server, possibly making it unavailable.  This problem can be mitigated by representing OuterExtensions in a way that allows it to be searched more quickly.  For example, the runtime can be improved to O(N*log(M)) by sorting the OuterExtensions and using binary search to access it. </del>  5.2. It MUST offer to negotiate TLS 1.3 or above. If it compressed any extensions in EncodedClientHelloInner, it <del> MUST copy the corresponding extensions from ClientHelloInner. </del>  It MUST copy the legacy_session_id field from ClientHelloInner. This allows the server to echo the correct session ID for TLS

This is an alternative to , which constrains the client such this suggestion is feasible: URL NAME thoughts? This does constrain the client behavior, though our draft implementation would not be affected by it. (We run through all the extensions in our usual order and compute the inner value + whether to compress it. If we intend to compress it, we write it to a separate buffer instead of the pending ClientHelloInner. Then we drop in all the buffered extensions, so they're contiguous and, in the EncodedClientHelloInner, write echouter_extensions instead of the buffer. This process keeps the relative order of compressed extensions, so we follow this rule anyway.)
Same here (wrt liking:-). Cheers, S.
Moved to appendix.
NAME can you please rebase?
Rebased
A naive implementation of decoding the inner ClientHello can easily go quadratic when processing outerextensions. Thus far, nothing in TLS has required searching for O(N) extensions, only O(implemented extensions). It's likely a lot of TLS implementations handle the extensions list linearly most of the time. outerextensions is now the first time you have to search for extensions you may not have implemented. A naive implementation would then go quadratic, which is a server DoS risk. It doesn't need to go quadratic (for instance, you can sort ClientHelloOuter's extensions and then binary search per lookup), but it's probably worth at least calling this out, if not switching to a simpler decoding scheme. (For a simpler decoder, we already require CHInner group the compressed extensions consecutively. We could do the same with CHOuter, at which point we only need to reference one byte slice, be it by offset/length or some goofier scheme.)
Switching to a simpler scheme: yes please. Just dropping the compression idea entirely: even better:-) Given this optimisation is really mostly for PQ schemes, I'd argue it'd be better to just drop the current idea and have a more constrained one, e.g. using a bit mask as the value of outer_extensions, so implementation know exactly which extensions can ever be copied from the outer CH. And I'd further argue we can leave defining all of that until later too. The current scheme also imposes quite a testing burden. At least with OpenSSL most extensions have some side-effect and testing that all combinations of those are handled correctly for the inner CH isn't trivial. (That's partly down to old code being a bit awful but I guess won't be simple for any code base.) Cheers, S.
Not sure I follow the concern with side effects. Are you processing the extensions as part of decompression? In order to make the handshake work, you'd have to construct the ClientHello by bytes anyway. Thus it seems the natural implementation would be to resolve ECH early, before ClientHello processing, then compute the inner ClientHello as bytes, then run the handshake with the resulting ClientHello. This aligns nicely with the model in split mode, etc. The issue I have in mind is simply that the decoding process, a pure function from a pair of byte strings to a byte string, is easy to accidentally make inefficient.
It occurs to me this might cause some conflicts between wanting ECH-ful and ECH-less ClientHellos to look similar (don't stick out) and pressures on ClientHello extension order. The order should be irrelevant, but compat concerns have cropped up before. (If only we required extensions be sorted...) Another possibility for a simpler decode would be if outer_extensions listed indexes into the extension list, rather than extension IDs. Though we would need to answer silly questions like whether it's an index into ClientHelloOuter or ClientHelloOuterAAD.
Since this is more of an implementation detail, let's just add a warning to handle this responsibly (i.e. the sort and binary search).
Yet another way to address this is with a simple hash map, e.g.: URL My vote is to leave the encoding scheme as-is and simply leave a warning.
NAME NAME could one of you please propose text for that warning?
Another DoS risk to be careful of: implementations typically enforce size limits on TLS messages so they aren't forced to buffer 2^24 bytes. Whatever size limit is enforced on ClientHello needs to also be enforced on the inner ClientHello before allocating memory for it. In particular, if ClientHelloOuter has a single giant extension, and it is referenced multiple times in OuterExtensions, you can blow up the server's resource usage. (Though I can't think of any case without repeating an extension where the resulting size grows dramatically.)
Is there any valid reason why an extension would appear multiple times in a ClientHello? If not, maybe we should specify that the server must abort if OuterExtensions references an extension more than once.
Seems reasonable. I think the combination of "no duplicate extensions" and "no referencing the ECH extension" imply that sizeof(ClientHelloInner) < sizeof(ClientHelloOuter).
Keeping this opening to track NAME and NAME comments on CH sizes. NAME could you please send a PR if you have cycles?
Discussed in 110: Will propose text to address the second vector and then close this issue.
I'm not going to nitpick this, but I like it. We currently have the same triply-nested loop here: the outer loop iterates over extensions in CHInner for most extensions we just copy them into a new structure for echouter we have two more layers of loop the middle loop iterates over the items in echouter the inner loop iterates over CHOuter, looking for a match This wouldn't really reduce code complexity, but it would mean that we wouldn't need to start over every time for that innermost loop. So we would be able to run in linear time as this says. Like you, as we iterate over extensions in the same order when constructing messages, this wouldn't have any effect on generating a ClientHello. My only concern with this is that it is somewhat more prescriptive than I would generally like for a specification. Moving this algorithmic stuff to an appendix might be a good balance. Either way, if you are adding this much text, it needs a section heading.LGTM pending suggestions!Works for me!

the client may use the "ech_outer_extensions" extension. OuterExtensions consists of one or more ExtensionType values, each of  <ins> which reference an extension in ClientHelloOuter.  The extensions in OuterExtensions MUST appear in ClientHelloOuter in the same relative order, however, there is no requirement that they be continguous. For example, OuterExtensions may contain extensions A, B, C, while ClientHelloOuter contains extensions A, D, B, C, E, F. The "ech_outer_extensions" extension is only used for compressing the ClientHelloInner.  It can only be included in EncodedClientHelloInner, and MUST NOT be sent in either ClientHelloOuter or ClientHelloInner. </ins> When sending ClientHello, the client first computes ClientHelloInner, including any PSK binders.  It then computes a new value, the 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  <ins> ClientHelloOuter.  The server MUST abort the connection with an "illegal_parameter" alert if any of the following are true: </ins>  <ins> Any referenced extension is missing in ClientHelloOuter. "encrypted_client_hello" appears in OuterExtensions. OuterExtensions contains duplicate values. The extensions in ClientHelloOuter corresponding to those in OuterExtensions do not occur in the same order. </ins>  <ins> Implementations SHOULD bound the time to compute a ClientHelloInner proportionally to the ClientHelloOuter size.  If the cost are disproportionately large, a malicious client could exploit this in a denial of service attack.  linear-outer-extensions describes a linear-time procedure that may be used for this purpose. </ins> 5.2. It MUST offer to negotiate TLS 1.3 or above. If it compressed any extensions in EncodedClientHelloInner, it  <ins> MUST copy the corresponding extensions from ClientHelloInner.  The copied extensions additionally MUST be in the same relative order as in ClientHelloInner. </ins> It MUST copy the legacy_session_id field from ClientHelloInner. This allows the server to echo the correct session ID for TLS


exceed this length if, e.g., the server uses wildcard names or added new names to the anonymity set. <del> The non-empty name of the client-facing server, i.e., the entity trusted to update the ECH configuration.  This is used to correct </del>  misconfigured clients, as described in handle-server-response. <del> This value MUST NOT begin or end with an ASCII dot and MUST be parsable as a dot-separated sequence of LDH labels, as defined in RFC5890, Section 2.3.1.  Clients MUST ignore any "ECHConfig" structure whose "public_name" does not meet these criteria.  Note that these criteria are incomplete; they incidentally rule out textual representations of IPv6 addresses (see RFC3986, Section 3.2.2), but do not exclude IPv4 addresses in standard dotted-decimal or other non-standard notations such as octal and hexadecimal (see RFC3986, Section 7.4).  If "public_name" contains a literal IPv4 or IPv6 address, the client SHOULD ignore the "ECHConfig" to avoid sending a non-compliant "server_name" extension on the ClientHelloOuter (see RFC6066, Section 3). </del>  A list of extensions that the client must take into consideration when generating a ClientHello message.  These are described below (config-extensions).  The "HpkeKeyConfig" structure contains the following fields: A one-byte identifier for the given HPKE key configuration.  This It MUST include an "encrypted_client_hello" extension with a payload constructed as described in encrypting-clienthello.  [[OPEN ISSUE: We currently require HRR-sensitive parameters to match in ClientHelloInner and ClientHelloOuter in order to simplify client- side logic in the event of HRR.  See https://github.com/tlswg/draft- respond with an empty Certificate message, denoting no client certificate.  Note that authenticating a connection for the public name does not authenticate it for the origin.  The TLS implementation MUST NOT report such connections as successful to the application.  It

This combines and , requiring that validation of publicname happen somewhere_, providing a reference parser (if one does not exist), and allowing it to happen when parsing ECHConfig. , cc NAME NAME NAME NAME
The max for a label in a DNS name is 63 octets though isn't it?
Yeah, but RFC6066 has no similar requirements (that I can tell), so I don't see we need to impose anything new here. Maybe I'm missing something? And if so, can you please propose text?
Fair ask:-) I guess the public_name mostly has to go in a cert SAN so would it help to use or refer to rfc5280 text? The most relevant bit there seems to be: When the subjectAltName extension contains a domain name system label, the domain name MUST be stored in the dNSName (an IA5String). The name MUST be in the "preferred name syntax", as specified by Section 3.5 of [RFC1034] and as modified by Section 2.1 of [RFC1123]. Note that while uppercase and lowercase letters are allowed in domain names, no significance is attached to the case. In addition, while the string " " is a legal domain name, subjectAltName extensions with a dNSName of " " MUST NOT be used. (That text is near [1]). That said, that " " is a legal domain name surprises me (and I'm a co-author of 5280!) BTW, I'm fine that that all gets fixed later if that's better. S. [1] URL
Sounds good -- I dropped an OPEN ISSUE to note this for later. NAME would you have cycles to take that issue and propose a PR to address it?
Sneaky eh:-) Sure, I can look at it this week but don't wait on me. S
Yeah, there's no rush here, I think. This seems like a smaller issue to resolve at some point.
For at least command line clients (e.g. curl) and perhaps for censorship circumvention tools it makes sense to allow the outer SNI to contain nothing or something that is not the publicname from the SVCB RR. It's possible that any tool that acquires an ECHConfig from a non-DNS source would benefit from that option too. The offending (for me:-) text is in section 6.1, 2nd list, bullet 7 where it says: 'The value of ECHConfig.contents.publicname MUST be placed in the "server_name" extension.' There may be clients that do need the above. I'm not sure how to chacterise those in a short phrase, but that's likely possible. At the IETF-110 TLS session mention was made of clients that need to fallback but I'm not sure what reference would be correct for that if e.g. we appended something like "URL clients that need to be able to fallback. [ref]"
Censorship circumvention isn't one of the stated goals of ECH. Even if an ECHConfig is acquired from a non-DNS source, it still has the ECHConfig.contents.publicname field, which, as specified in 6.1.3.3, is necessary because: "The client MUST verify that the certificate is valid for ECHConfig.contents.publicname. If invalid, it MUST abort the connection with the appropriate alert."
I believe we should keep this as-is. Fallback is for the server's benefit and the client has no way of knowing whether it will be needed.
+1 -- for robustness reasons, I believe we should keep this as-is.
I also found this requirement odd. Is it really necessary with the changes to config_id in draft -10? If someone could explain why this should be mandatory, that would be fine. I just don't understand the requirement.
When there is a mismatch between DNS and the server configuration, the server will handshake with the outer ClientHello. The expectation when that happens is that the connection is authenticated for the public name, which gives an authenticated recovery signal. That means the server needs to, e.g., select a certificate that covers the public name. Thus, the outer SNI selection.
OK, and a server that didn't need the outer SNI name would be identifiable by IP address anyway. Makes sense.
Sure - the right URL MUST be valid for the client to act upon failed ECH. I don't see that that implies that the ech.public_name MUST be sent though. S.
Hiya, I understand the sensitivities there but nor are we forced to make life harder for those who develop such applications. (Separately, I don't recall there being any consensus call related to whether or not that is or is not a goal.) A requirement for the client to verify the URL vs. the presented cert does not need to involve the publicname. I'm fine that the client MUST (as always) verify the presented cert vs. the URL if ECH didn't work. I'd also be fine with a SHOULD send here, or with a "MUST default to send publicname in URL" but an absolute MUST is not necessary afaics. The issue for me is for a generic library - in that case being able to override the public_name seems like it may sometimes be useful and since there's no need to require library developers to enforce the MUST, it's a bad plan I think. Cheers, S.
Hiya, Not convinced. For a web browser talking to a web server given a sensible set of DNS RRs, I do agree with you. For any random TLS client/server pair, I'm not yet convinced. Hence I still consider an absolute MUST OTT/wrong. S.
One oddity of this requirement is that it presents a GREASE distinguisher when the client is connecting to an IP address with no server name. In the GREASE case, the ClientHelloOuter.servername is absent because . In the real-ECH case, ClientHelloOuter.servername is present and contains ECHConfig.public_name. However, why use ECH at all if the IP address uniquely identifies the desired service? I think I support keeping the requirement for the sake of the fallback/retry mechanism. Was there some other scenario you're thinking of?
(The outer name pretty fundamentally interacts with GREASE because the distribution of true SNIs is different from the distribution of cleartext ones, but the fact that IP addresses don't get SNIs is an insignificant component of that, given how rare they are. Any reasonable model of GREASE distinguishability would need to be a bit more subtle.)
If possible, I'd like to see if we can drill down on where folks disagree. Fact 1: The public name specifies the name that the client expects the client-facing server to verify in case of ECH rejection. Fact 2: The outer SNI specifies the name the client expects the client-facing server to verify, regardless of whether ECH was offered. It seems like the point of disagreement is how these two extensions --- ECH and SNI --- are meant to be composed. It seems to me that NAME believes that SNI takes precedence: if we allowed public name != outer SNI, then this would violate the rules of the SNI extension, It seems like NAME believes that ECH can take precedence: allowing public name != outer SNI is OK because the client already knows what name the client-facing server will verify. Am I understanding this all correctly?
I'm very confused what facts 1 and 2 are trying to say. The servername extension, in any ClientHello, is the name of the server you're trying to contact with that ClientHello. This figures into certificate selection, and you could even imagine a protocol which uses it in lieu of an in-band Host header for virtual hosting. (Although HTTP does it in-band and doesn't care much about the servername.) In the ClientHelloInner, you are trying to contact the true name, so you put the true name in servername. In the ClientHelloOuter, you are trying to contact the public name, so you put the public name in servername. Independent of ECH, if you have external knowledge that such-and-such field will be interpreted in a different way and the server doesn't need the server_name, then you can go against the spec. If that external knowledge applies to the ClientHelloOuter, then you can apply that to the ClientHelloOuter. But that's not how the field is meant to work, so the spec should describe the intended semantics.
This seems to advocate that a SHOULD, rather than a MUST, is appropriate in choosing what name goes in ClientHelloOuter, right? In particular, the spec does not accommodate external knowledge about what goes in the field. It requires it be set to ECHConfig.public_name.
I think a SHOULD is correct. Cheers, S.
Fact 1 says that publicname is the name of the server when ECH is rejected (i.e., the client-facing server). Fact 2 says that servername is the name of the server. My point was merely that these facts appear to contradict one another if publicname != CHOuter.servername. Hence, we need to decide how to compose ECH with SNI in a way that resolves the contradiction. The answer might simply be that publicname MUST equal CHOuter.servername. Or, we might do say something else.
(I don't particular care about MUST vs. SHOULD. Whatever we say elsewhere for servername I guess.) NAME I'm not following. I think talking about publicname != CHOuter.servername is getting causality the wrong way around. CHOuter is a construct we are inventing in ECH. That means we get to define the semantics. The semantics we've defined are that CHOuter, along with the result of handshaking with CHOuter, is intended for the ECH rejection case. How does the client know how the ECH rejection case works? Well, we also need to define that in ECH. Our definition is that you handshake against ECHConfig.publicname. Finally, servername is a construct that has already been defined. What goes in there? RFC6066 and RFC8446 tell you. It's the name of the server you are trying to contact. With all that, what do you put in CHOuter.servername? Well, you put the rules together. It's the name of the server you are trying to contact in the ECH rejection case, which is ECHConfig.public_name.
I do:-) SHOULD seems correct to me. A MUST over-constrains generic libraries. I agree with the above but will quibble with some text below... Not really - CHOuter has to be a "working" CH with semantics defined by 8446, so we're a lot more constrained in reality. Yep. That's too absolute IMO. ECHConfig.publicname tells you a name that should work for the CHOuter fallback. There will be many situations where another name can work as well, though using ECHConfig.publicname is a fine default/SHOULD. Cheers, S.
Oh sure. I wasn't suggesting CHOuter and the resulting handshake get to ignore 8446. But the parameters to that handshake are not set and that's what ECH get to define. Hrm, in which link of the chain were you looking to add flexibility? There's: The implementation wants to set servername to some value other than the name they are contacting The implementation wants to set the name they are contacting to some value other than ECHConfig.publicname Weakening the first to SHOULD is perhaps plausible, especially if 6066 and 8446 already are less prescriptive. Weakening the second to SHOULD is much more dangerous because you lose the chain of authenticity up from the TLS handshake through X.509 to the name specified by the ECHConfig.
Let me try another way to justify a SHOULD. Consider SMTP/TLS. There can be >1 MX RR value for a domain. When forwarding a mail to NAME it may (or may not) be that the correct way to handle ECH for SMTP/TLS is to look for an SVCB at URL or at one of the MX names that we find at URL It may (or may not) be the case that some or all of the server-side TLS endpoints for those MX names share the same ECHConfig private values at a given moment. It may (or may not) be the case that the DNS answers for type65 at all those MX names and URL are well managed and always in sync. It may (and it will:-) be the case that multiple MX name type65 RRs and multiple SVCB RR values including future new SVCB or ECHConfig extensions interact in ways the TLS WG can't now forsee. I think there could in future be a real use-case for ECH in SMTP/TLS, and that it'd be wrong for the TLS WG to decide now with a MUST how ECHConfig.publicname ought be handled in all cases. I reckon many other protocols using TLS might also each have a pile of wrinkles we can't analyse. So I argue that at least generic libraries for TLS ought not be prohibited from having some kind of override that results in URL != ECHConfig.publicname. (And for at least some generic libraries, contravening such a MUST would I think be a reason why upstream might reject an offered PR.) All that said, using ECHConfig.public_name SHOULD work in almost all cases so is a good default and one to probably encourage. Hope that helps, S. PS: As an aside: some MX names expose what we'd like to put in URL, whereas some will not, and a simple sub- string match won't always catch that. But that's an issue for some mail folks to consider later, if it's a real issue. (It probably isn't for TLS, but maybe is for how MTAs might use multiple DoX queries.)

exceed this length if, e.g., the server uses wildcard names or added new names to the anonymity set.  <ins> The DNS name of the client-facing server, i.e., the entity trusted to update the ECH configuration.  This is used to correct </ins> misconfigured clients, as described in handle-server-response.  <ins> Clients MUST ignore any "ECHConfig" structure whose public_name is not parsable as a dot-separated sequence of LDH labels, as defined in RFC5890, Section 2.3.1 or which begins or end with an ASCII dot. Clients SHOULD ignore the "ECHConfig" if it contains an encoded IPv4 address.  To determine if a public_name value is an IPv4 address, clients can invoke the IPv4 parser algorithm in WHATWG- IPV4.  It returns a value when the input is an IPv4 address. See auth-public-name for how the client interprets and validates the public_name. </ins> A list of extensions that the client must take into consideration when generating a ClientHello message.  These are described below (config-extensions). <ins> [[OPEN ISSUE: determine if clients should enforce a 63-octet label limit for public_name]] [[OPEN ISSUE: fix reference to WHATWG-IPV4]] </ins> The "HpkeKeyConfig" structure contains the following fields: A one-byte identifier for the given HPKE key configuration.  This It MUST include an "encrypted_client_hello" extension with a payload constructed as described in encrypting-clienthello. <ins> Note that these rules may change in the presence of an application profile specifying otherwise. </ins> [[OPEN ISSUE: We currently require HRR-sensitive parameters to match in ClientHelloInner and ClientHelloOuter in order to simplify client- side logic in the event of HRR.  See https://github.com/tlswg/draft- respond with an empty Certificate message, denoting no client certificate. <ins> In verifying the client-facing server certificate, the client MUST interpret the public name as a DNS-based reference identity.  Clients that incorporate DNS names and IP addresses into the same syntax (e.g.  RFC3986, Section 7.4 and WHATWG-IPV4) MUST reject names that would be interpreted as IPv4 addresses.  Clients that enforce this by checking and rejecting encoded IPv4 addresses in ECHConfig.contents.public_name do not need to repeat the check at this layer. </ins> Note that authenticating a connection for the public name does not authenticate it for the origin.  The TLS implementation MUST NOT report such connections as successful to the application.  It


IANA is requested to create the following three entries in the existing registry for ExtensionType (defined in RFC8446): <del> encrypted_client_hello(0xfe0a), with "TLS 1.3" column values set </del>  to "CH, HRR, EE", and "Recommended" column set to "Yes". ech_outer_extensions(0xfd00), with the "TLS 1.3" column values set

cc/ NAME
I don't think we need to also bump echouterextensions but just in case... S
ECHConfig is defined as but the text below it says The intention was to bump ECHConfig.version from 0xfe0a to 0xfe0b for draft-11, but this got missed by URL (I wrote the PR, so I apologize for my part in missing this.) How do folks think we should resolve this? Do we need to cut a new draft with the ambiguity fixed?
Oh whoops, nice catch! Using definitely won't work since the format and corresponding ClientECH extension has changed. I suppose all the implementors could just agree to mentally fix this typo when reading the draft, but may as well cut draft-12. Draft numbers are cheap.
We could try to pick up some editorial changes, too, like those suggested in .
I'd also personally be pretty happy to have in there, but also fine to skip it for what's supposed to be a small bugfix draft snapshot.
+1 I've not checked recent editorial changes but they're probably fine too. S
I'd be happy to take in draft-12 as well, though I'm inclined to think this should strictly be a bugfix/editorial draft. I'd say let's wait until Chris W. gets off back from PTO and let him make the call. Unless we're in a hurry, in which case we could ping one of the other authors to merge our changes and cut a draft.
Closing as resolved.

IANA is requested to create the following three entries in the existing registry for ExtensionType (defined in RFC8446):  <ins> encrypted_client_hello(0xfe0b), with "TLS 1.3" column values set </ins> to "CH, HRR, EE", and "Recommended" column set to "Yes". ech_outer_extensions(0xfd00), with the "TLS 1.3" column values set


IANA is requested to create the following three entries in the existing registry for ExtensionType (defined in RFC8446): <del> encrypted_client_hello(0xfe0b), with "TLS 1.3" column values set </del>  to "CH, HRR, EE", and "Recommended" column set to "Yes". ech_outer_extensions(0xfd00), with the "TLS 1.3" column values set

To be included with . Note that this is a wire-breaking change that should have landed in draft-11, but didn't.
NAME since this will go into the next version of the draft, can we bump everything to the next version here?
Done.
Thanks!
ECHConfig is defined as but the text below it says The intention was to bump ECHConfig.version from 0xfe0a to 0xfe0b for draft-11, but this got missed by URL (I wrote the PR, so I apologize for my part in missing this.) How do folks think we should resolve this? Do we need to cut a new draft with the ambiguity fixed?
Oh whoops, nice catch! Using definitely won't work since the format and corresponding ClientECH extension has changed. I suppose all the implementors could just agree to mentally fix this typo when reading the draft, but may as well cut draft-12. Draft numbers are cheap.
We could try to pick up some editorial changes, too, like those suggested in .
I'd also personally be pretty happy to have in there, but also fine to skip it for what's supposed to be a small bugfix draft snapshot.
+1 I've not checked recent editorial changes but they're probably fine too. S
I'd be happy to take in draft-12 as well, though I'm inclined to think this should strictly be a bugfix/editorial draft. I'd say let's wait until Chris W. gets off back from PTO and let him make the call. Unless we're in a hurry, in which case we could ping one of the other authors to merge our changes and cut a draft.
Closing as resolved.

IANA is requested to create the following three entries in the existing registry for ExtensionType (defined in RFC8446):  <ins> encrypted_client_hello(0xfe0c), with "TLS 1.3" column values set </ins> to "CH, HRR, EE", and "Recommended" column set to "Yes". ech_outer_extensions(0xfd00), with the "TLS 1.3" column values set


7. Servers that support ECH play one of two roles, depending on the <del> payload of the "encrypted_client_hello" extension in the ClientHello: </del>  If "ClientECH.type" is "outer", then the server acts as a client- facing server and proceeds as described in client-facing-server to server overwrites the payload of the "encrypted_client_hello" extension with the following string: <del> As above, the payload of "encrypted_client_hello" is expected to be a "ClientECH" with "ClientECH.type" is "inner".  If this is not the case, the backend server MUST abort the handshake with an "illegal_parameter" alert. Note that, in case of HelloRetryRequest, the backend server confirms ECH acceptance twice: first In HelloRetryRequest, as described here; and then in the subsequent ServerHello, as described above. </del>  8.

The requirement to check the ClientHello for an inner ClientECH doesn't make sense because the entire section is already conditioned by observing an inner ClientECH. Also clarify that the client-facing vs. backend server dispatch happens at the initial ClientHello. Switching roles mid-connection will not work very well.

7. Servers that support ECH play one of two roles, depending on the  <ins> payload of the "encrypted_client_hello" extension in the initial ClientHello: </ins> If "ClientECH.type" is "outer", then the server acts as a client- facing server and proceeds as described in client-facing-server to server overwrites the payload of the "encrypted_client_hello" extension with the following string:  <ins> In the subsequent ServerHello message, the backend server sends the accept_confirmation value as described in backend-server. </ins> 8.


"encrypted_client_hello" appears in OuterExtensions. <del> OuterExtensions contains duplicate values. </del> The extensions in ClientHelloOuter corresponding to those in OuterExtensions do not occur in the same order.

There are two reasons for this. First, the algorithm in Appendix B for decoding the EncodedClientHelloInner doesn't actually enforce this. Second, aborting on duplicated extensions is slightly stricter than RFC 8446 requires. It states that "[t]here MUST NOT be more than one extension of the same type in a given extension block", but doesn't specify what the receiver does in this case.

"encrypted_client_hello" appears in OuterExtensions.  The extensions in ClientHelloOuter corresponding to those in OuterExtensions do not occur in the same order.


"ECHConfigContents.cipher_suites" list. The HPKE encapsulated key, used by servers to decrypt the <del> corresponding "payload" field.  This field is empty in ClientHelloOuters sent in response to HelloRetryRequest. </del>  The serialized and encrypted ClientHelloInner structure, encrypted using HPKE as described in real-ech. Next, the client determines the length L of encrypting EncodedClientHelloInner with the selected HPKE AEAD.  This is typically the sum of the plaintext length and the AEAD tag length. <del> The client fills in a "encrypted_client_hello" extension with the outer variant of ClientECH with following values: </del>  "config_id", the identifier corresponding to the chosen ECHConfig structure; generates a random PSK identity with the same length.  It also generates a random, 32-bit, unsigned integer to use as the "obfuscated_ticket_age".  Likewise, for each inner PSK binder, the <del> client generates random string of the same length. </del>  <del> If the server replies with a "pre_shared_key" extension in its </del>  ServerHello, then the client MUST abort the handshake with an "illegal_parameter" alert. a "decode_error" alert if it is invalid.  It otherwise ignores the extension and MUST NOT use the retry keys. <del> [[OPEN ISSUE: if the client sends a GREASE "encrypted_client_hello" extension, should it also send a GREASE "pre_shared_key" extension? If not, GREASE+ticket is a trivial distinguisher.  See issue #384.]] </del> Offering a GREASE extension is not considered offering an encrypted ClientHello for purposes of requirements in client-behavior.  In particular, the client MAY offer to resume sessions established

Just some minor nits I picked up during a slow pass of the spec.
LGTM pending one nit.

"ECHConfigContents.cipher_suites" list. The HPKE encapsulated key, used by servers to decrypt the  <ins> corresponding "payload" field.  This field is empty in a ClientHelloOuter sent in response to HelloRetryRequest. </ins> The serialized and encrypted ClientHelloInner structure, encrypted using HPKE as described in real-ech. Next, the client determines the length L of encrypting EncodedClientHelloInner with the selected HPKE AEAD.  This is typically the sum of the plaintext length and the AEAD tag length.  <ins> The client fills in an "encrypted_client_hello" extension with the outer variant of ClientECH with the following values: </ins> "config_id", the identifier corresponding to the chosen ECHConfig structure; generates a random PSK identity with the same length.  It also generates a random, 32-bit, unsigned integer to use as the "obfuscated_ticket_age".  Likewise, for each inner PSK binder, the  <ins> client generates a random string of the same length. </ins>  <ins> Per the rules of real-ech, the server is not permitted to resume a connection in the outer handshake.  If ECH is rejected and the client-facing server replies with a "pre_shared_key" extension in its </ins> ServerHello, then the client MUST abort the handshake with an "illegal_parameter" alert. a "decode_error" alert if it is invalid.  It otherwise ignores the extension and MUST NOT use the retry keys.  Offering a GREASE extension is not considered offering an encrypted ClientHello for purposes of requirements in client-behavior.  In particular, the client MAY offer to resume sessions established


8.2. <del> A more serious problem is MITM proxies which do not support this extension.  RFC8446, Section 9.3 requires that such proxies remove any extensions they do not understand.  The handshake will then present a certificate based on the public name, without echoing the "encrypted_client_hello" extension to the client. </del>  Depending on whether the client is configured to accept the proxy's certificate as authoritative for the public name, this may trigger connection failure.  A proxy which is not authoritative for the public name cannot forge a signal to disable ECH. <del> A non-conformant MITM proxy which instead forwards the ECH extension, substituting its own KeyShare value, will result in the client-facing server recognizing the key, but failing to decrypt the SNI.  This causes a hard failure.  Clients SHOULD NOT attempt to repair the connection in this case. </del> 9. In the absence of an application profile standard specifying

This makes a few changes to the middlebox section: First, using "MITM proxies" when RFC8446 talks about terminating TLS was confusing, so align with the RFC8446 terminology. Next, RFC8446 does not quite require dropping unknown extensions, only generating your own ClientHello. Dropping unknown extensions happens if the proxy tries to intersect its true capabilities with that of the client. I've also dropped the "a more serious problem" introduction since the point of this section is that it's not a serious problem. Finally, I've dropped the "non-conformant MITM proxy" paragraph. It's slightly out of date now that we've unified everything around trial decryption. I believe, depending on what the proxy does with the server's EncryptedExtensions response, things will either break, work on accident, or retry loop and hit the limit. It doesn't seem worth expending a whole lot of text on something that is the proxy's fault. This does drop the "Clients SHOULD NOT attempt to repair the connection in this case" line, but the client couldn't really detect this case anyway. It looks like any old connection failure without a proxy, and the usual considerations for changing your TLS config on connection failure apply.
Ah good point. I dropped the "which do not support this extension" clause on accident. How's this new iteration? (Seems best not to get into middleboxes which do support the extension. "which likely means being involved in" feels too handwaivy, and I'd rather not expend a lot of text around how DNS works with HTTP CONNECT proxies, CDNs where the "proxy" is on the server's side and controls DNS, etc.)
Hello, While reading this specification, section 8.2. states that "MITM proxies" are required to remove any extensions they do not understand. extension. [RFC8446], Section 9.3 requires that such proxies remove any extensions they do not understand. The handshake will then present a certificate based on the public name, without echoing the "encryptedclienthello" extension to the client. However, looking at RFC 8446 Section 9.3, the first section you encounter on forwarding middleboxes explicitly states the opposite: the middlebox is not allowed to change or modify any parameters in the handshake and subsequent traffic if it doesn't understand some parameter in the ClientHello: understand MUST NOT process any messages beyond that ClientHello. It MUST forward all subsequent traffic unmodified. Otherwise, it may fail to interoperate with newer clients and servers. Reading further, we find the bullet point the warning in section 8.2 refers to: compliant TLS server (to the original client), including having a certificate which the client is willing to accept, and also as a compliant TLS client (to the original server), including verifying the original server's certificate. In particular, it MUST generate its own ClientHello containing only parameters it understands, and it MUST generate a fresh ServerHello random value, rather than forwarding the endpoint's value. Here, the middlebox isn't so much required to "remove any extension they do not understand", but to "generate its own ClientHello containing only parameters it understands". After carefully reading over these sections multiple times, I do get the message, but I feel like it could be better worded as not to create confusion. The words "MITM proxy" could refer to the forwarding middlebox as well. Changing the wording in, for example, the following way makes the reference more clear in my opinion: extension. [RFC8446], Section 9.3 requires that such proxies generate their own ClientHello containing only parameters they understand. The middlebox will then present a certificate based on the public name to the client, without echoing the "encryptedclienthello" extension. What do you think about this?
Yes. Or, more accurately, it's required to send a ClientHello only containing things it supports. Whether it choses to base its capabilities on the original ClientHello is its business. Though, of course, there are security implications for either option and more reason why MITM proxies are a terrible idea. Either way, if you are a MITM proxy, you are terminating TLS, which means there are two TLS handshakes and, in one of them, you are a client. When acting as a client, you cannot send things you don't understand. Otherwise the server will respond in a way that confuses you. RFC 8446 does not discuss "forwarding middleboxes" at all. That section talks about a "middlebox which forwards ClientHello parameters it does not understand". Any middlebox that does this cannot terminate TLS in the first place (i.e. not a MITM proxy) because anyone terminating TLS is limited to only sending things it understands. Rather, this section is talking about non-terminating proxies that attempt to inspect the cleartext portions of TLS anyway. We ran into quite a lot of those in the process of deploying TLS 1.3 and most of them were broken. This text clarifies that TLS promises no continuity of syntax after the ClientHello. A non-terminating proxy gets to assume it can parse the ClientHello and nothing else. See above. If you're forwarding things you understand, you cannot terminate TLS. The section doesn't apply to what this draft refers to as "MITM proxy" at all. (Well, there are some weird things that try to selectively proxy TLS. There are with this approach, but in that case which rules apply depends on which choice the proxy made. Additionally, the proxy cannot violate the forwarding middlebox rule in the process of making the decision. This basically boils down to saying you cannot make your decision based on anything after the ClientHello because, after that point, you cannot parse anything.) Probably we should change this draft to match RFC 8446 in terminology and replace "MITM proxies" with "TLS-terminating proxies". Yeah, I think that change is a good one.
Thank you for your detailed reply. I do understand the reasons for being this strict about middlebox behaviour. I certainly don't think there is anything wrong. It's just that the terminology is somewhat confusing. After careful reading, it's indeed clear that the section on non-terminating proxies is not really relevant for ESNI, but due to the wording used in this RFC, it looks at first glance that 8.2 is referring to that section: vs It's a small detail, and not really that important. But I think it would make it more clear if the wording is changed slightly. I do like "TLS-terminating proxies" as well. Thanks for the hard work!
How does URL look?
Looks good to me! Thank you for taking the time to look at this.
The middlebox could also support ECH, right? It would need a way to get a config, and probably would need to be involved in feeding a config too the client, but it is at least possible for interception to consume and use ECH. Maybe qualify with "unless the middlebox understands ECH and is able to interpret the extension (which likely means being involved in providing the client with an ECHConfig)..."

8.2.  <ins> When connecting through a TLS-terminating proxy that does not support this extension, RFC8446, Section 9.3 requires the proxy still act as a conforming TLS client and server.  The proxy must ignore unknown parameters, and generate its own ClientHello containing only parameters it understands.  Thus, when presenting a certificate to the client or sending a ClientHello to the server, the proxy will act as if connecting to the public name, without echoing the "encrypted_client_hello" extension. </ins> Depending on whether the client is configured to accept the proxy's certificate as authoritative for the public name, this may trigger connection failure.  A proxy which is not authoritative for the public name cannot forge a signal to disable ECH.  9. In the absence of an application profile standard specifying


perhaps the most sensitive, unencrypted information in TLS 1.3. The target domain may also be visible through other channels, such as <del> plaintext client DNS queries, visible server IP addresses (assuming the server does not use domain-based virtual hosting), or other indirect mechanisms such as traffic analysis.  DoH RFC8484 and DPRIVE RFC7858 RFC8094 provide mechanisms for clients to conceal DNS lookups from network inspection, and many TLS servers host multiple domains on the same IP address.  In such environments, the SNI remains the primary explicit signal used to determine the server's identity. The TLS Working Group has studied the problem of protecting the SNI, but has been unable to develop a completely generic solution. RFC8744 provides a description of the problem space and some of the proposed techniques.  One of the more difficult problems is "Do not stick out" (RFC8744, Section 3.4): if only sensitive or private services use SNI encryption, then SNI encryption is a signal that a client is going to such a service.  For this reason, much recent work has focused on concealing the fact that the SNI is being protected. Unfortunately, the result often has undesirable performance consequences, incomplete coverage, or both. The protocol specified by this document takes a different approach. It assumes that private origins will co-locate with or hide behind a provider (reverse proxy, application server, etc.) that protects sensitive ClientHello parameters, including the SNI, for all of the domains it hosts.  These co-located servers form an anonymity set wherein all elements have a consistent configuration, e.g., the set of supported application protocols, ciphersuites, TLS versions, and so on.  Usage of this mechanism reveals that a client is connecting to a particular service provider, but does not reveal which server from the anonymity set terminates the connection.  Thus, it leaks no more than what is already visible from the server IP address. </del>  This document specifies a new TLS extension, called Encrypted Client <del> Hello (ECH), that allows clients to encrypt their ClientHello to a supporting server.  This protects the SNI and other potentially sensitive fields, such as the ALPN list RFC7301.  This extension is only supported with (D)TLS 1.3 RFC8446 and newer versions of the protocol. </del>  2.

It is rather long. I've taken a pass at making it read a little clearer, hopefully. I've tried to preserve the bits of the old text that seemed important. Hopefully other folks agree. This still could use another editing pass (e.g. the first and second paragraphs both conclude with the same thing), but I think it's an improvement. I've dropped the paragraph about about how the TLSWG has studied the problem but had difficulties. That seemed more relevant when we were comparing this approach against others. The following paragraph then describes the setup (colocated servers) and what the extension achieves (stuff about anonymity sets). Thats redundant with the text above talking about how sometimes the server IP isn't identifying, and the text below talking about what the extension does. I've worked that text into those two paragraphs. I tweaked the bit about consistent TLS configuration, since it's not actually true: it's fine if the servers don't support the same ALPN protocols. It's just a few externally-visible bits of TLS configuration. That's probably worth defining more accurately, if we haven't already (I forget), but not in the introduction. I've also dropped the claim that we leak no more than the server IP. We haven't addressed padding of application data, and cases where the colocated servers have sufficiently different TLS configuration are out of scope. I tried to general tightened up language. For instance there was a paragraph that says "(assuming the server does not use domain-based virtual hosting)" and then goes straight to talk about servers that do use virtual hosting.

perhaps the most sensitive, unencrypted information in TLS 1.3. The target domain may also be visible through other channels, such as  <ins> plaintext client DNS queries or visible server IP addresses. However, DoH RFC8484 and DPRIVE RFC7858 RFC8094 provide mechanisms for clients to conceal DNS lookups from network inspection, and many TLS servers host multiple domains on the same IP address.  Private origins may also be deployed behind a common provider, such as a reverse proxy.  In such environments, the SNI remains the primary explicit signal used to determine the server's identity. </ins> This document specifies a new TLS extension, called Encrypted Client  <ins> Hello (ECH), that allows clients to encrypt their ClientHello to such a deployment.  This protects the SNI and other potentially sensitive fields, such as the ALPN list RFC7301.  Co-located servers with consistent externally visible TLS configurations, including supported versions and cipher suites, form an anonymity set.  Usage of this mechanism reveals that a client is connecting to a particular service provider, but does not reveal which server from the anonymity set terminates the connection. ECH is only supported with (D)TLS 1.3 RFC8446 and newer versions of the protocol. </ins> 2.


The DNS name of the client-facing server, i.e., the entity trusted to update the ECH configuration.  This is used to correct <del> misconfigured clients, as described in handle-server-response. </del>  Clients MUST ignore any "ECHConfig" structure whose public_name is not parsable as a dot-separated sequence of LDH labels, as defined report such connections as successful to the application.  It additionally MUST ignore all session tickets and session IDs presented by the server.  These connections are only used to trigger <del> retries, as described in handle-server-response.  This may be implemented, for instance, by reporting a failed connection with a dedicated error code. </del>  6.1.5. all, it will ignore it as required by Section 4.1.2 of RFC8446. Provided the server can present a certificate valid for the public name, the client can safely retry with updated settings, as described <del> in handle-server-response. </del>  Unless ECH is disabled as a result of successfully establishing a connection to the public name, the client MUST NOT fall back to using Depending on whether the client is configured to accept the proxy's certificate as authoritative for the public name, this may trigger <del> the retry logic described in handle-server-response or result in a connection failure.  A proxy which is not authoritative for the public name cannot forge a signal to disable ECH. </del>  9.

Handling server response discusses how to dispatch between accept/reject, but the retry flow is specifically the reject bit.

The DNS name of the client-facing server, i.e., the entity trusted to update the ECH configuration.  This is used to correct  <ins> misconfigured clients, as described in rejected-ech. </ins> Clients MUST ignore any "ECHConfig" structure whose public_name is not parsable as a dot-separated sequence of LDH labels, as defined report such connections as successful to the application.  It additionally MUST ignore all session tickets and session IDs presented by the server.  These connections are only used to trigger  <ins> retries, as described in rejected-ech.  This may be implemented, for instance, by reporting a failed connection with a dedicated error code. </ins> 6.1.5. all, it will ignore it as required by Section 4.1.2 of RFC8446. Provided the server can present a certificate valid for the public name, the client can safely retry with updated settings, as described  <ins> in rejected-ech. </ins> Unless ECH is disabled as a result of successfully establishing a connection to the public name, the client MUST NOT fall back to using Depending on whether the client is configured to accept the proxy's certificate as authoritative for the public name, this may trigger  <ins> the retry logic described in rejected-ech or result in a connection failure.  A proxy which is not authoritative for the public name cannot forge a signal to disable ECH. </ins> 9.


handshake.  This is referred to as accepting ECH. Upon receiving the server's response, the client determines whether <del> or not ECH was accepted (handle-server-response) and proceeds with the handshake accordingly.  When ECH is rejected, the resulting </del>  connection is not usable by the client for application data. Instead, ECH rejection allows the client to retry with up-to-date configuration (rejected-ech). generated as described in grease-ech.) The client then constructs EncodedClientHelloInner as described in <del> encoding-inner.  Finally, it constructs the ClientHelloOuter message just as it does a standard ClientHello, with the exception of the following rules: </del>  It MUST offer to negotiate TLS 1.3 or above. ClientHelloOuter to safely ignore any early data sent by the client per RFC8446, Section 4.2.10. <del> It MUST include an "encrypted_client_hello" extension with a payload constructed as described in encrypting-clienthello. </del> Note that these rules may change in the presence of an application profile specifying otherwise. extensions are not offered in the ClientHelloOuter.  See outer- clienthello for additional guidance. <del> 6.1.1. To construct the "encrypted_client_hello", the client first determines the encapsulated key and HPKE encryption context.  If constructing the first ClientHelloOuter, it computes them as: </del>  <del> If constructing the second ClientHelloOuter (client-hrr), it reuses the encryption context computed for the first ClientHelloOuter, and sets "enc" to the empty string.  Note that the HPKE context maintains a sequence number, so this operation internally uses a fresh nonce for each AEAD operation.  Reusing the HPKE context avoids an attack described in flow-hrr-hijack. </del>  <del> The client then computes ClientHelloOuterAAD (authenticating-outer) by constructing a ClientHello with all other extensions determined as in real-ech. </del>  <del> Next, the client determines the length L of encrypting </del>  EncodedClientHelloInner with the selected HPKE AEAD.  This is typically the sum of the plaintext length and the AEAD tag length. <del> The client fills in an "encrypted_client_hello" extension with the outer variant of ECHClientHello with the following values: </del>  "config_id", the identifier corresponding to the chosen ECHConfig structure; "cipher_suite", the client's chosen cipher suite; <del> "enc", as computed above; and </del>  "payload", a placeholder byte string containing L zeros. ClientHelloOuter. The client serializes this structure to construct the <del> ClientHelloOuterAAD.  It then computes the payload as: </del>  Finally, the client replaces "payload" with "final_payload" to obtain ClientHelloOuter.  The two values have the same length, so it is not 6.1.4. <del> As described in server-behavior, the server MAY either accept ECH and use ClientHelloInner or reject it and use ClientHelloOuter.  In handling the server's response, the client's first step is to determine which value was used. </del>  <del> If the server replied with a HelloRetryRequest, then the client proceeds as described in client-hrr.  Otherwise, if the server replied with a ServerHello, then the client checks if the last 8 bytes of "ServerHello.random" are equal to "accept_confirmation" as defined in backend-server.  If so, then it presumes acceptance. Otherwise, the client presumes rejection. </del>  <del> 6.1.4.1. </del>  <del> If the server used ClientHelloInner, the client proceeds with the connection as usual, authenticating the connection for the true server name. </del>  <del> 6.1.4.2. </del>  <del> If the server used ClientHelloOuter, the client proceeds with the handshake, authenticating for ECHConfig.contents.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 configurations. </del>  Otherwise, if both authentication and the handshake complete successfully, the client MUST abort the connection with an "encrypted_client_hello" extension.  If the client does not retry in either scenario, it MUST report an error to the calling application. <del> 6.1.4.3. </del>  <del> When the server rejects ECH or otherwise ignores "encrypted_client_hello" extension, it continues with the handshake using the plaintext "server_name" extension instead (see server- behavior).  Clients that offer ECH then authenticate the connection with the public name, as follows: </del>  The client MUST verify that the certificate is valid for ECHConfig.contents.public_name.  If invalid, it MUST abort the instance, by reporting a failed connection with a dedicated error code. <del> 6.1.5. 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.  [[NOTE: This may change, depending on the outcome of issue#450.]]) Otherwise it proceeds as follows. 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. [[OPEN ISSUE: Depending on what we do for issue#450, it may be appropriate to change the client behavior if the HRR payload is missing or malformed.]] The client encodes the second ClientHelloInner as in encoding-inner, using the second ClientHelloOuter for any referenced extensions.  It then encrypts the new EncodedClientHelloInner value as a second message with the previous HPKE context as described in encrypting- clienthello. </del> 6.2. If the client attempts to connect to a server and does not have an scheme and L is the size of the EncodedClientHelloInner the client would compute when offering ECH, padded according to padding. <del> When sending a second ClientHello in response to a HelloRetryRequest, </del>  the client copies the entire "encrypted_client_hello" extension from the first ClientHello. <del> [[OPEN ISSUE: The above doesn't match HRR handling for either ECH acceptance or rejection.  See issue https://github.com/tlswg/draft- ietf-tls-esni/issues/358.]] </del>  <del> If the server sends an "encrypted_client_hello" extension, the client MUST check the extension syntactically and abort the connection with a "decode_error" alert if it is invalid.  It otherwise ignores the extension and MUST NOT use the retry keys. </del>  Offering a GREASE extension is not considered offering an encrypted ClientHello for purposes of requirements in real-ech.  In particular,

This PR cleans up the client text, in hopes of making it more precise and organized somewhat more chronologically. It then uses this cleanup to clarify bits we forgot to write down in the accept/reject/HRR machinery: Handling the Server Response is now Determining ECH Acceptance and only discusses the initial accept/reject decision. The portion of Handling HelloRetryRequest discussing the accept/reject decision is moved to Determining ECH Acceptence. Determining ECH Acceptance is tidied up to discuss to what happens if you see an older TLS version (it's a reject). In particular, in DTLS 1.2, the first server message might be HelloVerifyRequest and not ServerHello or HelloRetryRequest. Accepted/Rejected ECH are now Handshaking with ClientHelloInner/ClientHelloOuter to fit with the other section names (lots of VERBing). They are unindented from (1) because that section only talks about the accept/reject decision. This gives us a place to discuss all changes to the handshake. We describe what it actually means to handshake with ClientHelloInner, including implications to the transcript. And... URL portion of Handling HelloRetryRequest that discussed constructing the second ClientHello never applied to the ECH Reject case anyway, only ECH Accept. Move this to (4). Also expand on it so it's a bit more clearly defined. I've also moved HPKE setup out of Encrypting the ClientHello, as that's only relevant to one of the two "callers" anyway. I'm not thrilled with this notion of "partial ClientHelloOuterAAD", but the text used in Offering ECH and Encrypting the ClientHello sections otherwise doesn't actually match. Before this PR, both think they are responsible for constructing ClientHelloOuter. With this and (1), Handling HelloRetryRequest is now empty and removed. We forgot to write down the client half of URL There is now a place for it in Handshaking with ClientHelloInner. We say that the ECH reject case ignores the URL extension, so ECH GREASE says the same thing, to keep them aligned. The Handshaking with ClientHelloOuter section could also do with an editorial pass, but I've omitted it from this PR because it was getting a bit large as it is.
I suspect this is editorial, but it would be helpful to clarify exactly how the client computes its transcript when it generates its keys, based on the permutations of who gets what. IIUC: If the server authenticates the inner SNI, the client must construct the client hello as seen by the back-end server, including any OuterExtensions compression, and uses it in the transcript. If the server authenticates the public SNI, the client uses the entire client hello in the hash as-is, including the (unreadable) encryptedclienthello extension. A server that combines the client-facing and back-end functions also has to duplicate this logic, since it can see everything but maybe shouldn't use it all, and has to go through the motions of formally constructing a proper client hello to a back-end server. I found this all quite subtle to work out and asked some questions of one of the authors to fully understand. I'll leave it to the editors as to whether the draft needs some text to make this clearer. [Aside: is it computationally feasible to add some sort of "checksum" that would neutralize the difference in the hashes? That would make this a little less tricky, IMO]
+1 and test vectors would be good too
Yes, agree. This is really tricky: "The client presumes acceptance if the last 8 bytes of URL are equal to the first 8 bytes of accept_confirmation as defined in Section 7.2. Otherwise, it presumes rejection."
Here's what I ended up writing after I solved this: "This issue is that the ECH transcript needs to be calculated from an unsent inner ClientHello message, which differs in a few ways from the EncodedClientHelloInner used in HPKE. Upon ECH acceptance, the transcript from the ClientHelloOuter is discarded, then substituted with one calculated from the unsent inner ClientHello message, and the client random is changed to the inner random value. After that, the ServerHello message is added to the transcript as normal. The transcript used for ECH acceptance is separate from the normal transcript."
I think the way to think about this is that there are two possible handshakes here, joined at the first message. Either we handshake with ClientHelloOuter, or we handshake with ClientHelloInner. Whichever, we handshake with, we follow the usual RFC8446 rules, including incorporating the chosen ClientHello into the transcript. Indeed on the server, the protocol even supports a client-facing vs. backend server split where the connection is actually rewritten to use ClientHelloInner. EncodedClientHelloInner, per the name, is just an encoding. We don't want to send two copies of things like key shares. The actual ClientHello being conveyed is still ClientHelloInner. (This is important since the transcript needs to reflect all inputs to TLS parameter negotiation.) But, yes, on the client this gets a little fun because the client needs to offer these two somewhat related ClientHellos, learn which was to used a message later, and then complete the handshake accordingly.
Perhaps there's missing text in "6.1.4.1. Accepted ECH", where client transcript handling could be addressed. My feedback is that it was possible to construct a conformant EncodedClientHelloInner and enclosing ClientHelloOuter from reading the spec as a non-author, but I had to test with other implementations to discover what I was supposed to do with the ClientHelloInner. If you get this part wrong, then you just hit a decryption error after ServerHello. I did understand that I had to update the transcript, but I initially thought I should do it with an EncodedClientHelloInner added to the ClientHelloOuter transcript. Once I discovered that was not the case, I had to construct a ClientHelloInner that still had the fields that are compressed in EncodedClientHelloInner. That doesn't cross anything in the text, but it wasn't obvious to me.
I think we're in agreement here, but is the encoding of ClientHelloInner deterministic enough that the client-facing server can't possibly express the expanded ClientHelloInner in a way that breaks the transcript?
It's decode, not encode, that needs to be deterministic. And yes, it is deterministic.
Here's what I ended up writing to get this working. I don't have HRR support done yet, so I expect these comments will change a little bit:
Which transcript is used should be better clarified now that landed. Closing as such. (If we want to track test vectors for ECH, let's open a new issue.)
Thanks for capping the amount of changes in this PR :)

handshake.  This is referred to as accepting ECH. Upon receiving the server's response, the client determines whether  <ins> or not ECH was accepted (determining-ech-acceptance) and proceeds with the handshake accordingly.  When ECH is rejected, the resulting </ins> connection is not usable by the client for application data. Instead, ECH rejection allows the client to retry with up-to-date configuration (rejected-ech). generated as described in grease-ech.) The client then constructs EncodedClientHelloInner as described in  <ins> encoding-inner.  It also computes an HPKE encryption context and "enc" value as: Next, it constructs a partial ClientHelloOuterAAD as it does a standard ClientHello, with the exception of the following rules: </ins> It MUST offer to negotiate TLS 1.3 or above. ClientHelloOuter to safely ignore any early data sent by the client per RFC8446, Section 4.2.10.  Note that these rules may change in the presence of an application profile specifying otherwise. extensions are not offered in the ClientHelloOuter.  See outer- clienthello for additional guidance.  <ins> Finally, the client encrypts the EncodedClientHelloInner with the above values, as described in encrypting-clienthello, to construct a ClientHelloOuter.  It sends this to the server, and processes the response as described in determining-ech-acceptance. </ins>  <ins> 6.1.1. </ins>  <ins> Given an EncodedClientHelloInner, an HPKE encryption context and "enc" value, and a partial ClientHelloOuterAAD, the client constructs a ClientHelloOuter as follows. </ins>  <ins> First, the client determines the length L of encrypting </ins> EncodedClientHelloInner with the selected HPKE AEAD.  This is typically the sum of the plaintext length and the AEAD tag length.  <ins> The client then completes the ClientHelloOuterAAD with an "encrypted_client_hello" extension.  This extension value contains the outer variant of ECHClientHello with the following fields: </ins> "config_id", the identifier corresponding to the chosen ECHConfig structure; "cipher_suite", the client's chosen cipher suite;  <ins> "enc", as given above; and </ins> "payload", a placeholder byte string containing L zeros. ClientHelloOuter. The client serializes this structure to construct the  <ins> ClientHelloOuterAAD.  It then computes the final payload as: </ins> Finally, the client replaces "payload" with "final_payload" to obtain ClientHelloOuter.  The two values have the same length, so it is not 6.1.4.  <ins> As described in server-behavior, the server may either accept ECH and use ClientHelloInner or reject it and use ClientHelloOuter.  This is determined by the server's initial message. If the message does not negotiate TLS 1.3 or higher, the server has rejected ECH.  Otherwise, it is either a ServerHello or HelloRetryRequest. </ins>  <ins> If the message is a ServerHello, the client computes "accept_confirmation" as described in backend-server.  If this value matches the last 8 bytes of "ServerHello.random", the server has accepted ECH.  Otherwise, it has rejected ECH. </ins>  <ins> If the message is a HelloRetryRequest, the client checks for the "encrypted_client_hello" extension.  If none is found, the server has rejected ECH.  Otherwise, if it has a length other than 8, the client aborts the handshake with a "decode_error" alert.  Otherwise, the client computes "hrr_accept_confirmation" as described in backend- server-hrr.  If this value matches the extension payload, the server has accept ECH.  Otherwise, it has rejected ECH. [[OPEN ISSUE: Depending on what we do for issue#450, it may be appropriate to change the client behavior if the HRR extension is present but with the wrong value.]] </ins>  <ins> If the server accepts ECH, the client handshakes with ClientHelloInner as described in accepted-ech.  Otherwise, the client handshakes with ClientHelloOuter as described in rejected-ech. </ins>  <ins> 6.1.5. </ins>  <ins> If the server accepts ECH, the client proceeds with the connection as in RFC8446, with the following modifications: The client behaves as if it had sent ClientHelloInner as the ClientHello.  That is, it evaluates the handshake using the ClientHelloInner's preferences, and, when computing the transcript hash (Section 4.4.1 of RFC8446), it uses ClientHelloInner as the first ClientHello. If the server responds with a HelloRetryRequest, the client computes the updated ClientHello message as follows: It computes a second ClientHelloInner based on the first ClientHelloInner, as in Section 4.1.4 of RFC8446.  The ClientHelloInner's "encrypted_client_hello" extension is left unmodified. It constructs EncodedClientHelloInner as described in encoding- inner. It constructs a second partial ClientHelloOuterAAD message.  This message MUST be syntactically valid.  The extensions MAY be copied from the original ClientHelloOuter unmodified, or omitted.  If not sensitive, the client MAY copy updated extensions from the second ClientHelloInner for compression. It encrypts EncodedClientHelloInner as described in encrypting- clienthello, using the second partial ClientHelloOuterAAD, to obtain a second ClientHelloOuter.  It reuses the original HPKE encryption context computed in real-ech and uses the empty string for "enc". The HPKE context maintains a sequence number, so this operation internally uses a fresh nonce for each AEAD operation.  Reusing the HPKE context avoids an attack described in flow-hrr-hijack. The client then sends the second ClientHelloOuter to the server. However, as above, it uses the second ClientHelloInner for preferences, and both the ClientHelloInner messages for the transcript hash.  Additionally, it checks the resulting ServerHello for ECH acceptance as in determining-ech-acceptance.  If the ServerHello does not also indicate ECH acceptance, the client MUST terminate the connection with an "illegal_parameter" alert. 6.1.6. If the server rejects ECH, the client proceeds with the handshake, authenticating for ECHConfig.contents.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 configurations. </ins> Otherwise, if both authentication and the handshake complete successfully, the client MUST abort the connection with an "encrypted_client_hello" extension.  If the client does not retry in either scenario, it MUST report an error to the calling application.  <ins> 6.1.7. </ins>  <ins> When the server rejects ECH, it continues with the handshake using the plaintext "server_name" extension instead (see server-behavior). Clients that offer ECH then authenticate the connection with the public name, as follows: </ins> The client MUST verify that the certificate is valid for ECHConfig.contents.public_name.  If invalid, it MUST abort the instance, by reporting a failed connection with a dedicated error code.  6.2. If the client attempts to connect to a server and does not have an scheme and L is the size of the EncodedClientHelloInner the client would compute when offering ECH, padded according to padding.  <ins> If sending a second ClientHello in response to a HelloRetryRequest, </ins> the client copies the entire "encrypted_client_hello" extension from the first ClientHello.  <ins> If the server sends an "encrypted_client_hello" extension in either HelloRetryRequest or EncryptedExtensions, the client MUST check the extension syntactically and abort the connection with a "decode_error" alert if it is invalid.  It otherwise ignores the extension.  It MUST NOT save the "retry_config" value in EncryptedExtensions. </ins>  <ins> [[OPEN ISSUE: Depending on what we do for issue#450, it may be appropriate to change the client behavior if the HRR extension is present.]] </ins> Offering a GREASE extension is not considered offering an encrypted ClientHello for purposes of requirements in real-ech.  In particular,


Otherwise, if all candidate ECHConfig values fail to decrypt the extension, the client-facing server MUST ignore the extension and <del> proceed with the connection using ClientHelloOuter.  This connection proceeds as usual, except the server MUST include the "encrypted_client_hello" extension in its EncryptedExtensions with the "retry_configs" field set to one or more ECHConfig structures with up-to-date keys.  Servers MAY supply multiple ECHConfig values of different versions.  This allows a server to support multiple versions at once. </del>  Note that decryption failure could indicate a GREASE ECH extension (see grease-ech), so it is necessary for servers to proceed with the 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 <del> there is one. </del>  Note that a client-facing server that forwards the first ClientHello cannot include its own "cookie" extension if the backend server sends

This assumes the formulation where, on ECH reject, the server is the client-facing server. In other words, backend servers will never get a CH without ECH having been accepted, so GREASE doesn't apply there. cc NAME
cc NAME too, since he had thoughts on this

Otherwise, if all candidate ECHConfig values fail to decrypt the extension, the client-facing server MUST ignore the extension and  <ins> proceed with the connection using ClientHelloOuter, with the following modifications: If sending a HelloRetryRequest, the server MAY include an "encrypted_client_hello" extension with a payload of 8 random bytes; see dont-stick-out for details. If the server is configured with any ECHConfigs, it MUST include the "encrypted_client_hello" extension in its EncryptedExtensions with the "retry_configs" field set to one or more ECHConfig structures with up-to-date keys.  Servers MAY supply multiple ECHConfig values of different versions.  This allows a server to support multiple versions at once. </ins> Note that decryption failure could indicate a GREASE ECH extension (see grease-ech), so it is necessary for servers to proceed with the 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  <ins> there is one.  Moreover, if the server is configured with any ECHConfigs, it MUST include the "encrypted_client_hello" extension in its EncryptedExtensions with the "retry_configs" field set to one or more ECHConfig structures with up-to-date keys, as described in client-facing-server. </ins> Note that a client-facing server that forwards the first ClientHello cannot include its own "cookie" extension if the backend server sends


DISCLAIMER: This draft is work-in-progress and has not yet seen significant (or really any) security analysis.  It should not be used <del> as a basis for building production systems. </del>  Although TLS 1.3 RFC8446 encrypts most of the handshake, including the server certificate, there are several ways in which an on-path

Seems useful to note that -13 should be around for a while but still might change later
Yeah that's fine. Ta, S.
How about this simplification based on the outcome from 111? I think it's clear that future changes may occur, so I just dropped that.

DISCLAIMER: This draft is work-in-progress and has not yet seen significant (or really any) security analysis.  It should not be used  <ins> as a basis for building production systems.  This published version of the draft has been designated an "implementation draft" for testing and interop purposes. </ins> Although TLS 1.3 RFC8446 encrypts most of the handshake, including the server certificate, there are several ways in which an on-path


A "HpkeKeyConfig" structure carrying the configuration information associated with the HPKE public key.  Note that this structure contains the "config_id" field, which applies to the entire <del> ECHConfigContents.  Sites MUST NOT publish two different "ECHConfigContents" values with the same "HpkeKeyConfig" value. The RECOMMENDED technique for choosing "config_id" is to choose a random byte.  This process is repeated if this config_id matches that of any valid ECHConfig, which could include any ECHConfig that has been recently removed from active use. </del>  The longest name of a backend server, if known.  If not known, this value can be set to zero.  It is used to compute padding A one-byte identifier for the given HPKE key configuration.  This is used by clients to indicate the key used for ClientHello <del> encryption. </del>  The HPKE KEM identifier corresponding to "public_key".  Clients MUST ignore any "ECHConfig" structure with a key using a KEM they 4.1.  ECH configuration extensions are used to provide room for additional functionality as needed.  See config-extensions-guidance for guidance on which types of extensions are appropriate for this structure.

NAME how does this look to you? The discussion on how to allocate configids made it very unclear whether we cared about collisions in DNS or across client-facing servers. (The former is insufficient and the latter is practically impossible.) Instead, pull this into a separate section, so we can be clear this is a recommendation for the client-facing server and the backend server. Also downgrade from MUST to SHOULD because collisions work fine, just don't perform as well. It was also weird that we talked about configid in not the section about config_ids, but in the little side note about HpkeKeyConfig.
I went with "known" instead of "active" since {{client-facing-server}} already used "known". It just never defined it, so I guess now we have a definition or sorts. :-)
That looks good, better than I'd suggested. I guess I'll close the other one in a few minutes (just to give someone time to correct me that that's not the right button-pushing:-) Thanks, S.
Middleboxes identifies a legitimate concern where middleboxes might strip a new ESNI extension that is unknown to them. The ServerName structure as defined in RFC 6066 has an 8-bit NameType field where 0 is defined as hostname and all other values are undefined. Why not define 1 (or any other value) as encryptedhostname? The name structure inside ServerName is 16 opaque bits which seems able to accommodate EncryptedSNI as defined in this document. (Make encryptedsni slightly smaller so that "label" and "suite" fit)
I prefer having a different extension, because we do not want to limit the capability of ESNIKeys to just protecting the "HostName" (as identified by the 8-bit NameType field of zero). IMO it is better to have a mechanism that can protect any type of "server-name", including those covered by NameTypes other than zero. Note that DNS is just one way of publishing ESNIKeys.
I agree with NAME
I didn't mean for ESNI to only apply to NameType 0. There are 256 values, 0 is hostname, 1 can be encryptedservername, and there are 254 more values for additional types of server name types. Going forward I doubt someone will define a plain text type that will have to be protected. But even if that happens encryptedserver_name can encapsulate all name types but itself. P.S.: Not really sure how the use of DNS is a factor here.
Recall that SNI extensibility is .
Fair enough. The Server Name Indication (SNI) extension in TLS has a provision to provide names other than host names[1]. None have even been defined to my knowledge, but it's there. OpenSSL (and possibly others) have had a long-standing bug[2] (fixed in master) that means that different types of names will cause an error. To be clear: I live in a glass house and am not throwing stones; these things happen. However, it means that a huge fraction of the TLS deployment will not be able to accept a different name type should one ever be defined. (This issue might have been caused by the fact that the original[3] spec didn't define the extension in such a way that unknown name types could be skipped over.) Therefore we (i.e. BoringSSL, and thus Google) are proposing to give up on this and implement our parser such that the SNI extension is only allowed to contain a single host name value. (This is compatible with all known clients.) We're assuming that since this is already the de-facto reality that there will be little objection. I'm sending this mostly to record the fact so that, if someone tries to define a new name type in the future, they won't waste their time. If the community wishes to indicate a different type of name in the future, a new extension can be defined. This is already effectively the case because we wouldn't fight this level of incompatibility when there's any other option. (I think the lesson here is that protocols should have a single joint, and that it should be kept well oiled. For TLS, that means that extensions should have minimal extensionality in themselves and that we should generally rely on the main extensions mechanism for these sorts of things.) [1] URL [2] URL – note that the data pointer is not updated. [3] URL Cheers AGL Adam Langley EMAIL URL

A "HpkeKeyConfig" structure carrying the configuration information associated with the HPKE public key.  Note that this structure contains the "config_id" field, which applies to the entire  <ins> ECHConfigContents. </ins> The longest name of a backend server, if known.  If not known, this value can be set to zero.  It is used to compute padding A one-byte identifier for the given HPKE key configuration.  This is used by clients to indicate the key used for ClientHello  <ins> encryption. config-ids describes how client-facing servers allocate this value. </ins> The HPKE KEM identifier corresponding to "public_key".  Clients MUST ignore any "ECHConfig" structure with a key using a KEM they 4.1. <ins> A client-facing server has a set of known ECHConfig values, with corresponding private keys.  This set SHOULD contain the currently published values, as well as previous values that may still be in use, since clients may cache DNS records up to a TTL or longer. client-facing-server describes a trial decryption process for decrypting the ClientHello.  This can impact performance when the client-facing server maintains many known ECHConfig values.  To avoid this, the client-facing server SHOULD allocate distinct "config_id" values for each ECHConfig in its known set.  The RECOMMENDED strategy is via rejection sampling, i.e., to randomly select "config_id" repeatedly until it does not match any known ECHConfig. It is not necessary for "config_id" values across different client- facing servers to be distinct.  A backend server may be hosted behind two different client-facing servers with colliding "config_id" values without any performance impact.  Values may also be reused if the previous ECHConfig is no longer in the known set. 4.2. </ins> ECH configuration extensions are used to provide room for additional functionality as needed.  See config-extensions-guidance for guidance on which types of extensions are appropriate for this structure.