Code change:
suite it will use for encryption.  It MUST NOT choose a cipher suite not advertised by the configuration. <del> Next, the client constructs the ClientHelloOuter message just as it </del> <ins> Next, the client constructs the ClientHelloInner message just as it </ins> does a standard ClientHello, with the exception of the following rules: <del> It MUST offer to negotiate TLS 1.3 or above. It MUST include an "encrypted_client_hello" extension with a payload constructed as described below. </del> <ins> It MUST NOT offer to negotiate TLS 1.2 or below.  Note this is necessary to ensure the backend server does not negotiate a TLS version that is incompatible with ECH. </ins> <del> The value of "ECHConfig.public_name" MUST be placed in the "server_name" extension. </del> <ins> It MUST NOT offer to resume any session for TLS 1.2 and below. </ins> <del> It MUST NOT include the "pre_shared_key" extension.  (See flow- clienthello-malleability.) </del> <ins> It SHOULD contain TLS padding RFC7685 as described in padding. </ins> <del> The client then constructs the ClientHelloInner message just as it </del> <ins> The client then constructs the ClientHelloOuter message just as it </ins> does a standard ClientHello, with the exception of the following rules: <del> It MUST NOT offer to negotiate TLS 1.2 or below.  Note this is necessary to ensure the backend server does not negotiate a TLS version that is incompatible with ECH. It MUST NOT offer to resume any session for TLS 1.2 and below. </del> <ins> It MUST offer to negotiate TLS 1.3 or above. </ins> <del> It MAY offer any other extension in the ClientHelloOuter except those that have been incorporated into the ClientHelloInner as described in outer-extensions. </del> <ins> Any extensions compressed as described in outer-extensions must match the ClientHelloInner.  [[OPEN ISSUE: When #331 and compression ordering is resolved, be a bit more precise here.]] </ins> <del> It MAY copy any other field from the ClientHelloOuter except ClientHelloOuter.random.  Instead, It MUST generate a fresh ClientHelloInner.random using a secure random number generator. </del> <ins> It MAY copy any other field from the ClientHelloInner except ClientHelloInner.random.  Instead, It MUST generate a fresh ClientHelloOuter.random using a secure random number generator. </ins> (See flow-client-reaction.) <del> It SHOULD contain TLS padding RFC7685 as described in padding. </del> If implementing TLS 1.3's compatibility mode (see Appendix D.4 of RFC8446), it MUST copy the legacy_session_id field from <del> ClientHelloOuter.  This allows the server to echo the correct </del> <ins> ClientHelloInner.  This allows the server to echo the correct </ins> session ID when ECH is negotiated. <ins> It MUST include an "encrypted_client_hello" extension with a payload constructed as described below. The value of "ECHConfig.public_name" MUST be placed in the "server_name" extension. It MUST NOT include the "pre_shared_key" extension.  (See flow- clienthello-malleability.) </ins> The client might duplicate non-sensitive extensions in both messages. However, implementations need to take care to ensure that sensitive extensions are not offered in the ClientHelloOuter.  [[OPEN ISSUE: We

Related discussion:
text: There are two ClientHelloInners, the actual one used in the handshake, and the encoded one with outerextensions substituted in. Both are described as ClientHelloInner, but this makes it slightly ambiguous which goes into, say, the handshake transcript. (It's late now, so I'll leave this as an issue without a PR. If we don't end up resolving this separately, I'll probably fix this as part of . If we decide to implicitly copy over legacysession_id, the encryption payload becomes less and less defensibly an actual ClientHelloInner.)
In my own implementation, I've been referring to the plaintext payload as CompressedClientHelloInner. Which is terrible, but unambiguous.
Works for me. I was thinking EncodedClientHelloInner because it's shorter and still works even if you choose not to compress anything. (A no-op encoding is still an encoding.)
+1 to EncodedClientHelloInner, given that compression is optional. NAME if you have time, could you whip up a PR?
I noticed this while drafting a PR for . We're a little inconsistent about whether the ClientHelloInner or ClientHelloOuter is constructed first. Section 4 says inner before outer: Section 5.1 implies inner before outer: But section 6.1 says outer before inner: I think inner before outer makes more sense. You certainly can't finish the outer one before you've computed the inner one. (Of course, an implementation can pick whatever order is most convenient, but I think the spec should be consistent with itself.)