Original text:
"ClientECH.cipher_suite", "ClientECH.config_id", and "ClientECH.enc" fields of the payload of the "encrypted_client_hello" extension.  The last parameter, "outer_hello", is computed by serializing <del> ClientHelloOuter with the "encrypted_client_hello" extension removed. Note this does not include the four-byte header included in the Handshake structure. </del>  <del> Note the decompression process in encoding-inner forbids </del>  "encrypted_client_hello" in OuterExtensions.  This ensures the unauthenticated portion of ClientHelloOuter is not incorporated into ClientHelloInner.

Reviewer comments:
Having looked at this, I have a preference for a construction where the outer ClientHello has a placeholder with empty extension data, rather than a placeholder that has an empty payload field. Zeroing out the payload field is not easier in our implementation. It is somewhat more difficult to implement. This is a preference only; anything can be implemented. But this version is preferable.
The position of the ECH extension is not authenticated as part of ClientHelloOuterAAD. This means that it is possible to construct multiple different ClientHello messages that will all be accepted. This probably isn't that interesting, but it might be worth noting.
If we want to fix this (I don't care either way), we can remove the fields prepended to ClientHelloOuterAAD, leave the ECH extension in, and say the AAD is computed by replacing ClientECH.payload with the empty string.
That seems like a reasonable proposal. NAME what do you think? (NAME might also have thoughts on this.)
I can live with either a placeholder or text in the draft that explains the limitation. Extension order doesn't carry any semantics, so I don't see malleability as a real risk. I'd lean toward the placeholder, but only slightly. More so if we were planning to change the AAD anyway.
This seems like a good idea to me.
If we don't really care about this issue, why specify it? Does this add complexity or reduce it?
We don't know of any way this can be a problem, but that doesn't mean there is none. Plus, malleability is generally not great. I don't think this moves the complexity needle in a meaningful way.
I guess it exposes an active distinguishing attack, though I suspect this style of attack has plenty of vectors: Attacker rewrites CH to move the ECH extension to another spot and forwards to server Server responds with SH..Finished. Attacker forwards that to client unmodified If this was negotiating CHOuter, this would break the transcript and the client would reject. If this was negotiating CHInner, the client won't notice and proceed
Yep, nice! Let's definitely make this change, then.

Revised text:
"ClientECH.cipher_suite", "ClientECH.config_id", and "ClientECH.enc" fields of the payload of the "encrypted_client_hello" extension.  The last parameter, "outer_hello", is computed by serializing  <ins> ClientHelloOuter with the "encrypted_client_hello" extension set to the empty string, i.e., the "extension_data" list has zero length. Note this serialization does not include the four-byte header included in the Handshake structure. </ins>  <ins> The decompression process in encoding-inner forbids </ins> "encrypted_client_hello" in OuterExtensions.  This ensures the unauthenticated portion of ClientHelloOuter is not incorporated into ClientHelloInner.