Original text:
MUST send when it offered an "encrypted_client_hello" extension that was not accepted by the server.  (See alerts.) <del> [[OPEN ISSUE: The following text is a remnant of the section on "ech_nonce".  It specifies client/server behavior that is unrelated to "ech_nonce".]] Per client-behavior and server-behavior, implementations are required to track, alongside each PSK established by a previous connection, whether ECH was accepted for this connection.  If so, this is referred to as an "ECH PSK".  Otherwise, it is a "non-ECH PSK".  This may be implemented by adding a new field to client and server session states. </del> 5.1. Some TLS 1.3 extensions can be quite large and having them both in <del> the inner and outer ClientHello will lead to a very large overall size.  One particularly pathological example is "key_share" with post-quantum algorithms.  In order to reduce the impact of duplicated extensions, the client may use the "outer_extension" extension. </del>  OuterExtensions MUST only be used in ClientHelloInner.  It consists of one or more ExtensionType values, each of which reference an The value of "ECHConfig.public_name" MUST be placed in the "server_name" extension. <del> It MUST NOT include the "pre_shared_key" extension.  [[OPEN ISSUE: The text needs to be updated to reflect this rule.]] </del>  The client then constructs the ClientHelloInner message just as it does a standard ClientHello, with the exception of the following necessary to ensure the backend server does not negotiate a TLS version that is incompatible with ECH. <del> It MUST NOT offer to resume any non-ECH PSK or any session for TLS 1.2 and below. </del>  It MAY offer any other extension in the ClientHelloOuter except those that have been incorporated into the ClientHelloInner as ClientHello, the client sends a second updated ClientHello per the rules in RFC8446.  However, at this point, the client does not know whether the server processed ClientHelloOuter or ClientHelloInner, <del> and MUST regenerate both values to be acceptable.  Note: if the inner and outer ClientHellos use different groups for their key shares or differ in some other way, then the HelloRetryRequest may actually be invalid for one or the other ClientHello, in which case a fresh ClientHello MUST be generated, ignoring the instructions in HelloRetryRequest.  Otherwise, the usual rules for HelloRetryRequest processing apply. </del>  Clients bind encryption of the second ClientHelloInner to encryption of the first ClientHelloInner via the derived ech_hrr_key by 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 case, it does not make any changes to the TLS messages, but just blindly forwards them. <del> If the server sends a NewSessionTicket message, the corresponding ECH PSK MUST be ignored by all other servers in the deployment when not </del>  negotiating ECH, including servers which do not implement this specification. own ticket and forwards the resulting ClientHello.  Assume the server then validates the PSK binder on the outer ClientHello and chooses connection parameters based on the inner ClientHello.  A server which <del> then validates information in the outer ClientHello ticket against information in the inner ClientHello, such as the SNI, introduces an </del>  oracle that can be used to test the encrypted SNI value of specific ClientHello messages. <del> ECH mitigates against this attack by requiring servers not mix-and- match information from the inner and outer ClientHello.  For example, if the server accepts the inner ClientHello, it does not validate binders in the outer ClientHello.  This means that ECH PSKs are used </del>  within the HPKE encryption envelope. 11. 12. <del> Any future information or hints that influence the outer ClientHello </del>  SHOULD be specified as ECHConfig extensions.  This is primarily because the outer ClientHello exists only in support of ECH.  Namely, it is both an envelope for the encrypted inner ClientHello and

Reviewer comments:
We need to decide if we want to allow a GREASE+ticket distinguisher, and if not, how we GREASE the "presharedkey" extension in a meaningful way. The latter seems like somewhat of a cat and mouse game, so I'm not really enthusiastic about trying to specify that behavior, but I'd like to hear what others think. cc NAME NAME NAME
This looks good. I still feel that there are a lot of ways that ECH usage might stick out, even to passive observers. "presharedkey" is one of many. If anything I would change the "OPEN ISSUE" to encompass top-to-bottom "don't stick out" review.
We previously decided not to bind the outer ClientHello to the ECH decryption, because doing so is rather a hassle. We'd need something similar to the TLS 1.3 PSK binder mess, with the added complexity of ordering relative to the PSK binders themselves. Additionally, while the binding helps with some of the don't stick out properties, there were other attacks on distinguishability anyway. So, instead, we mitigated the inner ClientHello malleability with OuterExtensions.innerdigest and left the outer ClientHello unbound. However, we've since decided not to allow PSKs in the outer ClientHello. Perhaps we should revisit this. Suppose the AD parameter to the ECH decryption were the entire outer ClientHello, minus the ECH extension. I believe this means we no longer need OuterExtensions.innerdigest. That would give an immediate 33-byte savings. It would then unlock a further 32 byte savings by amending to say: Client always encodes an empty legacysessionid in ECH plaintext. Client-facing server copies outer legacysessionid to inner ClientHello as part of decompression. (The reason we can't do this easily right now is that, if the inner ClientHello lacks outerextension, nothing's authenticating legacysessionid. Since the field in TLS 1.3 is all but ignored, it's "obvious" that the malleability doesn't matter, but it's nicer to not need to think about such things.) From looking at Chrome's ClientHellos and working through what we'd send (drop 1.2-only extensions, compress most), it looks like the inner ClientHello will be maybe 130-140 bytes, plus a padded servername. The 65 bytes above would almost halve the overhead. In some sense what's going on here is our 16-byte tag on the AEAD in the ECH ciphertext is going to waste. We didn't authenticate everything that goes into reconstructing the inner ClientHello (outerextensions), so instead we authenticate another hash (OuterExtensions.innerdigest) and check that afterwards. (To that end, if we do this, we probably need to say the server MUST reject encryptedclienthello showing up in outer_extensions, that way all inputs to decompression are authenticated. Equivalently, decompression is done relative to the ECH-extension-less AD input rather than the original outer ClientHello.)
I like this idea. How do you think the AD ought to be structured? The ClientHelloOuter, but with an an empty "encryptedclienthello" extension?
Yeah, that's seems the simplest to me. Filling it with zeros, as NAME pointed out to me, requires predicting the length of the encrypted inner ClientHello. This isn't strictly impossible (ADs don't change ciphertext length), but it seems needlessly fussy and ECH is already requiring we be able re-encode ClientHellos, lengths and all. Note this assumes there are no computed PSK binders in the outer ClientHello because otherwise there's a circular dependency. (If we wanted to allow that, the AD would need to zero/trim the binders or so.)
Another benefit is it stops the attacker from probing information about compressed extensions. Currently an attacker can learn which extensions are compressed in the inner ClientHello by removing them from the outer ClientHello and seeing if the server still responds. This isn't a huge deal, as compressed extensions are assumed public, but this way we remove that vector and leave only the fuzzier signal from the ciphertext length.
On second thought, a lot of implementations will need to do that prediction anyway. The workaround for the old F5 bug means we'll need to know the eventual size for the padding extension. (On the other hand, you can probably assume anyone doing ECH is not running those things. On the other hand, turning off the workaround would stick out.) Ah well. We did it for PSK already. (I wonder if we can do drop that workaround now that TLS 1.0 and 1.1 are gone...)
What's the "old F5 bug"? What's the value in authenticating the length of the "encryptedclienthello" extension? This is authenticated by the handshake already, no?
URL URL It's why we have a padding extension. There isn't. It's just a question of what's easier to deal with. Filling it with zeros means that, when computing the AD on the receiving side, you don't need to fully reconstruct a modified outer ClientHello with updated length prefixes. We avoided this when computing the PSK binder in RFC8446. It's a mild hassle but not a huge deal. More importantly, we've thoroughly given up on that elsewhere in the spec. The F5 padding business also means computing this AD will likely take some length prediction anyway, but so it goes.
I agree, it's not a big hassle.
Rather than make the extension empty, can we just omit the and fields? (The and should arguably be included, a la ).
That works, though it's even more fussy for the client to construct, since it needs to half-encode the ECH extension before fully encoding it. is already bound into HPKE itself. is less clear, but I thought our model already said all this didn't matter? Isn't the binding here just to replace and provide malleability? If there's something more going on, I'd like us to have some principled answer here, so we can answer questions like... what about the handshake transcript, which may not be empty on HRR? If we do that, what's going on with echhrr_key?
The principle I was following is: include as much as possible in the AD. Yeah, it's more of a pain, and we've gotten away with no binding whatsoever up to now, so maybe we don't need it. I'm happy to start with an empty ECH extension and then consider the other parts later, if that'll help.
I don't feel very strongly either way.
Let's just do the simpler thing now, then! :-)
The current draft contains various rules around ECH and resumption. These rules date to before we started encrypting the entire ClientHello and were intended to avoid mixing up session resumptions between the public and private name, since the same presharedkey extension was used for both, and amount to saying implementations must add a boolean to their session state. Now that the public and private names correspond to different ClientHellos altogether, the client can (and should) only send inner name tickets in the inner ClientHello and outer name tickets in the outer ClientHello. () I suspect that means we no longer need further ECH special cases here.
This seems like a good simplification, but we should probably be careful about the interaction with cross-SNI resumption.
I think it just composes as expected? If you offer a PSK in the outer ClientHello, cross-SNI or no, it means that you are satisfied with it as an alternative to the certificate covering the public name. If you offer a PSK in the inner ClientHello, cross-SNI or no, it means you are satisfied with it as an alternative to the certificate covering the true name. Your ClientHellos should be consistent with this, which means that servers resuming isn't an issue. TBH I forgotten some of the details for why this was here. I found this long comment I wrote, but apparently past me was just unsatisfied that a case (4) was "weird". So that was very unhelpful, past me. Regardless, I think the ambiguity is moot now. URL (I also expect clients just won't offer PSKs in the outer ClientHellos. It's unlikely that you'll get any to offer anyway. And if your API has an external session cache, the caller needs to go out of their way to do two lookups.)
I was thinking about clients that might mistakenly use inner tickets for the outer name when given the cross-SNI signal. (I agree that it composes naturally.) A simple note would probably suffice. For outer tickets, I tend to think they should just be discouraged. What's the benefit of resuming a TLS 1.3 connection you'd tear down anyway?
I'm not sure I fully understand the (historical) need to distinguish between ECH-PSKs and non-ECH-PSKs. But why would a client offer PSK to a client-facing server at all? In other words, will it ever be useful to send a PSK extension in the ClientHelloOuter? It seems to me that this feature is only meaningful to the intended backend server, and so should only appear in the ClientHelloInner. The same question applies to all other extensions. If the client-facing server terminates the connection, then the only action the client will take is to close the connection. Hence, the only functionality we will ever need for the clientclient-facing server connection is to complete a full, server-authenticated handshake. If I were implementing the client, I would restrict the contents of the ClientHelloOuter to only those extensions that are necessary to implement this functionality. I wonder if it makes sense to enforce this in the spec. Something like: "When offering the 'ech' extension, the client MAY include the following extensions in the ClientHelloOuter: 'supportedversions', 'servername'. 'keyshares', 'signaturealgorithms', [and so on]. It MUST NOT include any other extension." What are the downsides to doing something like this? I think the upside is that it simplifies the problem of deciding what to put in the ClientHelloInner versus the ClientHelloOuter. I suppose one downside is that it potentially sticks out a bit.
Based on this week's interim, we decided it was fine to prohibit PSKs in the outer CH for ECH connections, so the current draft text prohibits "presharedkey" extensions in the outer CH. A connection using GREASE may, however, decide to resume its connection to the outer name. This gives a passive adversary a pretty easy distinguisher between GREASE and non-GREASE: if there's a ticket present, the connection is definitely GREASE. It seems that if we want to maintain our current indistinguishability properties, there always needs to be some ticket. So perhaps that means: ECH (non-GREASE) clients would generate a bogus (GREASEd) ticket GREASE clients that don't have a ticket would generate a bogus ticket GREASE clients that have a ticket would use it NAME NAME NAME NAME NAME penny for your thoughts?
I mostly figure there won't be a ticket in the initial connection to a server, or any connections made after long time, so intolerance to ticket-less ClientHellos will likely be noticed quickly. Though it is true that probably a lot of connections offer tickets. There may be something to be said for allowing clients to send bogus tickets if they want, but it's worth noting an active attacker can tell if the ticket is (most likely) bogus by simply trying to resume it. (A tangent: we actually saw a very old TLS 1.0 server implementation that broke with TLS 1.3 because it would take the client's session IDs and unconditionally echo it back. I'm not sure what was going on there. It didn't actually switch to a resumption handshake or implement resumption at all. It just echoed back the session ID for some reason. Only reason this didn't break before was that it never issued session IDs, so this codepath had never been tested before.)
True, but that's already the case for active attackers. What I'm concerned about are passive attackers.
I think this gets at a larger "don't stick out" problem currently only addressed in bits and pieces. Just as dummy (i.e., greased) ECH is used to provide covertext for real ECH, real-ECH connections also need to provide covertext for the real extensions offered in dummy-ECH connections. In particular, the spec should provide guidance on how to generate dummy extensions in the ClientHelloOuter. The following errata report has been verified for RFC8996, "Deprecating TLS 1.0 and TLS 1.1". You may review the report below and at: URL Status: Verified Type: Editorial Reported by: Gaëtan Leurent Date Reported: 2024-02-03 Verified by: RFC Editor Section: 10.2 Original Text [Bhargavan2016] Bhargavan, K. and G. Leuren, Corrected Text [Bhargavan2016] Bhargavan, K. and G. Leurent, Notes The last name "Leurent" is misspelt is the references. RFC8996 (draft-ietf-tls-oldversions-deprecate-12) Title : Deprecating TLS 1.0 and TLS 1.1 Publication Date : March 2021 Author(s) : K. Moriarty, S. Farrell Category : BEST CURRENT PRACTICE Source : Transport Layer Security Area : Security Stream : IETF

Revised text:
MUST send when it offered an "encrypted_client_hello" extension that was not accepted by the server.  (See alerts.)  5.1. Some TLS 1.3 extensions can be quite large and having them both in  <ins> ClientHelloInner and ClientHelloOuter will lead to a very large overall size.  One particularly pathological example is "key_share" with post-quantum algorithms.  In order to reduce the impact of duplicated extensions, the client may use the "outer_extension" extension. </ins> OuterExtensions MUST only be used in ClientHelloInner.  It consists of one or more ExtensionType values, each of which reference an The value of "ECHConfig.public_name" MUST be placed in the "server_name" extension.  <ins> It MUST NOT include the "pre_shared_key" extension.  (See flow- resumption-oracle.) </ins> The client then constructs the ClientHelloInner message just as it does a standard ClientHello, with the exception of the following necessary to ensure the backend server does not negotiate a TLS version that is incompatible with ECH.  <ins> It MUST NOT offer to resume any session for TLS 1.2 and below. </ins> It MAY offer any other extension in the ClientHelloOuter except those that have been incorporated into the ClientHelloInner as ClientHello, the client sends a second updated ClientHello per the rules in RFC8446.  However, at this point, the client does not know whether the server processed ClientHelloOuter or ClientHelloInner,  <ins> and MUST regenerate both values to be acceptable.  Note: if ClientHelloOuter and ClientHelloInner use different groups for their key shares or differ in some other way, then the HelloRetryRequest may actually be invalid for one or the other ClientHello, in which case a fresh ClientHello MUST be generated, ignoring the instructions in HelloRetryRequest.  Otherwise, the usual rules for HelloRetryRequest processing apply. </ins> Clients bind encryption of the second ClientHelloInner to encryption of the first ClientHelloInner via the derived ech_hrr_key by a "decode_error" alert if it is invalid.  It otherwise ignores the extension and MUST NOT use the retry keys. <ins> [[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.]] </ins> 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 case, it does not make any changes to the TLS messages, but just blindly forwards them.  <ins> If the server sends a NewSessionTicket message, the corresponding PSK MUST be ignored by all other servers in the deployment when not </ins> negotiating ECH, including servers which do not implement this specification. own ticket and forwards the resulting ClientHello.  Assume the server then validates the PSK binder on the outer ClientHello and chooses connection parameters based on the inner ClientHello.  A server which  <ins> then validates information in ClientHelloOuter ticket against information in ClientHelloInner, such as the SNI, introduces an </ins> oracle that can be used to test the encrypted SNI value of specific ClientHello messages.  <ins> ECH mitigates against this attack by (1) prohibiting the "pre_shared_key" extension in ClientHelloOuter and (2) requiring servers ignore this extension when processing ClientHelloOuter. Thus, if the server accepts the inner ClientHello, it only validates binders in the inner ClientHello.  This means that PSKs are used </ins> within the HPKE encryption envelope. 11. 12.  <ins> Any future information or hints that influence ClientHelloOuter </ins> SHOULD be specified as ECHConfig extensions.  This is primarily because the outer ClientHello exists only in support of ECH.  Namely, it is both an envelope for the encrypted inner ClientHello and