Original text:
The value of "ECHConfig.contents.public_name" MUST be placed in the "server_name" extension. <del> It MUST NOT include the "pre_shared_key" extension.  (See flow- clienthello-malleability.) </del>  [[OPEN ISSUE: We currently require HRR-sensitive parameters to match in ClientHelloInner and ClientHelloOuter in order to simplify client- 6.1.2.  This section describes a deterministic padding mechanism based on the following observation: individual extensions can reveal sensitive information through their length.  Thus, each extension in the inner EncryptedExtension, so that handshake message also needs to be padded using TLS record layer padding. <del> 6.1.3. </del>  As described in server-behavior, the server MAY either accept ECH and use ClientHelloInner or reject it and use ClientHelloOuter.  In of "accept_confirmation" as defined in backend-server.  Otherwise, it presumes rejection. <del> 6.1.3.1. </del>  If the server used ClientHelloInner, the client proceeds with the connection as usual, authenticating the connection for the true server name. <del> 6.1.3.2. </del>  If the server used ClientHelloOuter, the client proceeds with the handshake, authenticating for ECHConfig.contents.public_name as "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.3.3. </del>  When the server rejects ECH or otherwise ignores "encrypted_client_hello" extension, it continues with the handshake implemented, for instance, by reporting a failed connection with a dedicated error code. <del> 6.1.4. </del>  As required in real-ech, clients offering ECH MUST ensure that all extensions or parameters that might change in response to receiving a

Reviewer comments:
This may also be useful for . NAME do let me know if this PR overloads the term "GREASE" in a way that you find to be inaccurate or not in the GREASE spirit :) I'm perfectly happy to call it something else.
I don't know the answer to this question, but, from the TLS 1.3 RFC: I noticed this while testing with a pre-baked ClientHello that had both extensions. The ECH draft clearly required that the extension be removed, but I wondered about .
Given URL, URL, and URL, I'd say quite the opposite.
Ah, I see text has been added saying it must appear in the ClientHelloOuter. Thanks for the references. I was working from draft-10.
Here's a fun case. Suppose the client talks to a 0-RTT-capable, ECH-capable server. It'll send earlydata + presharedkey in ClientHelloInner, wrap that in some ClientHelloOuter, and follow it up with early data records. Now the server rejects ECH and handshakes with the ClientHelloOuter. But the client has already sent early data, so the server needs to know to skip past it. I believe this means we need to require the client mirror ClientHelloInner.earlydata in ClientHelloOuter, even though it's not actually offering to resume anything. Sending earlydata without presharedkey is slightly weird (if tolerated at all), so we might even want to recommend a fake outer presharedkey. We almost_ don't care about this: the client may as well stop the handshake at server Finished and skip the client Finished flight anyway, for purposes of the recovery flow. However: What's not what the draft currently says If the server rejects ECH, handshakes with ClientHelloOuter, and sends HelloRetryRequest, we do care about the client's second flight getting through. That second case is extra fun because the client-facing server must look for the second ClientHello too.
Wasn't this already the outcome from ? (That is, if the inner CH has a PSK, then the outer one ought have a dummy PSK, too.) As for mirroring earlydata, why can the server not just ignore the application data (as if it received earlydata and decided to ignore it)? Taking a quick look back at RFC8446, the server behavior where clients send application data without early_data is not defined, so are you concerned about that, or something else?
Well, I think we only got as far as MAY in , but yeah I think this is another reason. Regarding ignoring application data, our server implementation only skips early data if there was an early_data extension that we rejected. I believe that matches RFC8446 since it doesn't say to skip application data in general, just that case. And, yeah, that's the concern.
Since this is behavior for the client-facing server, which supports ECH, can't we specify the desired behavior in this spec? That is, we might say that a client-facing server which rejects ECH MUST ignore any early data sent by the server, or something. Would that work?
We could, but then it'd break the nice property that a plain RFC8446 server will handshake ClientHelloOuter without any fuss. This property is great for rollback safety. (Otherwise any ECH server deployment needs to be two-stage, and introduces a lower bound that you cannot rollback beyond.) And elsewhere we've tried to avoid contradicting RFC8446. Yet another option would be to tell the client to retry on certain kinds of errors if it offered 0-RTT in ClientHelloInner, but that's a bit more fuss since retries are often outside the TLS stack. (Besides, the fact that you offer 0-RTT in ClientHelloInner is all but public anyway. While we don't have a cleartext 0-RTT marker[], it's pretty obvious from timing that some post-ClientHello record came before ServerHello.) [] In TCP... I want to say QUIC does, but I could be misremembering.
True! This is fun. :-)
I'm not sure why ignoring stuff is fuss. What's the alternative? To terminate the handshake if the server receives early data? Wouldn't that be more fuss? If RFC8446 doesn't specify what to do in this situation, I'm not sure why specifying it is in contradiction to RFC8446 (as opposed to expanding on it).
I think RFC8446 does say what to do in this situation. Section 5.2 says at the bottom: URL The default for the record layer is that decryption errors are fatal (as it should be). Early data skipping is an exception to this described in section 4.2.10, and the exception only applies when the server declines an earlydata extension. Oh no, the goal is to ignore stuff. That's what makes the recovery flow work. The issue is that, in RFC8446, servers only ignore the early data if there was an earlydata extension in ClientHello which they declined. Otherwise, it's just a fatal decryption error. In order to meet that condition, ClientHelloOuter needs the early_data extension whenever ClientHelloInner does.
ACK -- thanks for closing the loop!
As discussed in , there are two real-ECH/grease-ECH differentiators related to the PSK extension we may wish to address. Real ECH disallows the PSK extension in ClientHelloOuter, whereas grease ECH permits PSK in ClientHello. Real ECH allows PSK in ClientHelloInner. In case of acceptance, the backend server may respond with PSK in ServerHello. The response would appear to be unsolicited. something that would not happen in a grease-ECH or vanilla handshake. The question we will ask ourselves here is if/how to mitigate these differentiators. We might simply say that clients MAY (or SHOULD?) send a dummy PSK in the ClientHello(Outer). This ought to provide sufficient cover.
I don't mind a MAY on sending a dummy PSK in case (2), though I don't think it's a good idea for case (1). The case that really matters here is when the outer and inner ClientHello both lack PSKs: Suppose the buggy network is sensitive to (1) and only allows ECH extensions when the outer ClientHello has a PSK because it knows those can't be ECH. That network has effectively broken all connections because the only way for the client to get a PSK is to have connected to the server without a PSK at some point. Suppose instead the buggy network is sensitive to (2) and breaks if the inner ClientHello has a PSK but the outer does not. The connect will fail, but between single-use tickets and bounded client session caches, the client will quickly exhaust its tickets and get out of case (2). This is a performance issue, but the connections still work. (Though, yeah, adding the option to send a dummy one might help there. shrug)
I'm not following the following point. Could you elaborate? In any case, the case to mitigate (2) seems more compelling than the case to mitigate (1), since (2) would look like non-compliance to an ECH-ignorant network. Would be curious to see what others think.
So I think the benefit of GREASE-like strategies is that broader breakages now are more noticeable than focused breakage later. More noticeable breakage is more likely to be fixed, and less likely to make it to the ecosystem in the first place. This applies to ECH GREASE by saying we want all ClientHellos to look like ECH was deployed, so the network is less likely to develop intolerances. Differences between GREASE ECH and actual ECH thus matter because the network be intolerant to something that we failed to simulate. (1) says that, in actual ECH, all outer ClientHellos have no PSK. In GREASE ECH, some outer ClientHellos have a PSK while some do not. If we were to translate that to actual intolerance, it would mean the network rejects outer ClientHellos without a PSK. But that would break everything, ECH or non-ECH, anyway because clients don't send PSKs the first time[] they connect to a server. Clients only have PSKs to send the second time, and you can't connect a second time if you didn't connect a first time. Thus, even though (1) is a difference, it doesn't seem that difference prevents us from noticing intolerance. Agreed that mitigating (2) is more compelling than mitigating (1). [] First time overall, first time after a restart (if sessions aren't persisted), or first time after a long break (sessions expired).
I agree that (2) is more important. And I'm fine with a "MAY/SHOULD send an outer dummy PSK if you are using ECH and have an inner PSK." NAME if we were to append on a dummy PSK, what do you think would be a suitable length? (Bikeshedding over that should be fun.) As an aside, I wanted to drill in on this point: This seems true, if we're assuming the the intolerance is "break the connection," which I think is the right interpretation. But it occurs to me that there's a slightly weaker form of misbehavior thats's possible with (1): a network may learn information about when a given client uses ECH. This seems out of scope, but maybe something to keep in mind?
Re length, I dunno leave it to the client to choose? "Probably not very long." Making that in scope sounds exceedingly difficult. For instance, for a given server, you might know that tickets issued today start with 12345 (maybe the ID of today's key), but synthesized dummy tickets won't match that.
Yep, no argument there.
Discussed in 110: Will submit a PR that says "clients MAY send a dummy PSK if the inner CH uses a PSK."

Revised text:
The value of "ECHConfig.contents.public_name" MUST be placed in the "server_name" extension.  <ins> When the client offers the "pre_shared_key" extension in ClientHelloInner, it SHOULD also include a GREASE "pre_shared_key" extension in ClientHelloOuter, generated in the manner described in grease-psk.  The client MUST NOT use this extension to advertise a PSK to the client-facing server.  (See flow- clienthello-malleability.)  When the client includes a GREASE "pre_shared_key" extension, it MUST also copy the "psk_key_exchange_modes" from the ClientHelloInner into the ClientHelloOuter. </ins> [[OPEN ISSUE: We currently require HRR-sensitive parameters to match in ClientHelloInner and ClientHelloOuter in order to simplify client- 6.1.2. <ins> When offering ECH, the client is not permitted to advertise PSK identities in the ClientHelloOuter.  However, the client can send a "pre_shared_key" extension in the ClientHelloInner.  In this case, when resuming a session with the client, the backend server sends a "pre_shared_key" extension in its ServerHello.  This would appear to a network observer as if the the server were sending this extension without solicitation, which would violate the extension rules described in RFC8446.  Sending a GREASE "pre_shared_key" extension in the ClientHelloOuter makes it appear to the network as if the extension were negotiated properly. The client generates the extension payload by constructing an "OfferedPsks" structure (see RFC8446, Section 4.2.11) as follows. For each PSK identity advertised in the ClientHelloInner, the client 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 client generates random string of the same length. If the server replies with a "pre_shared_key" extension in its SeverHello, then the client MUST abort the handshake with an "illegal_parameter" alert. 6.1.3. </ins> This section describes a deterministic padding mechanism based on the following observation: individual extensions can reveal sensitive information through their length.  Thus, each extension in the inner EncryptedExtension, so that handshake message also needs to be padded using TLS record layer padding.  <ins> 6.1.4. </ins> As described in server-behavior, the server MAY either accept ECH and use ClientHelloInner or reject it and use ClientHelloOuter.  In of "accept_confirmation" as defined in backend-server.  Otherwise, it presumes rejection.  <ins> 6.1.4.1. </ins> If the server used ClientHelloInner, the client proceeds with the connection as usual, authenticating the connection for the true server name.  <ins> 6.1.4.2. </ins> If the server used ClientHelloOuter, the client proceeds with the handshake, authenticating for ECHConfig.contents.public_name as "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.4.3. </ins> When the server rejects ECH or otherwise ignores "encrypted_client_hello" extension, it continues with the handshake implemented, for instance, by reporting a failed connection with a dedicated error code.  <ins> 6.1.5. </ins> As required in real-ech, clients offering ECH MUST ensure that all extensions or parameters that might change in response to receiving a