Change:
This isn't critical but I think it might be better to allow use of a single-shot HPKE API that doesn't need to first and separately generate a public/private pair before calling Seal(). The downside of forcing two HPKE calls is perhaps mostly that someone will be tempted to re-use that key pair for something else or a 2nd attempt with maybe bad effects. If someone ever did HPKE in h/w there might also be a security difference. I'm also not entirely sure what we're protecting by including that public value ("enc") in the AAD as well so that could be better documented at least, if we keep things as-is, but perhaps including that field is really a bit of overkill?
A single-shot HPKE already isn't viable because of HRR. You need to keep the second HPKE context around in case you need to encrypt second ClientHello. That said, I'm perfectly happy with omitting it. (Or including it.) This dates to URL and URL Of the added fields, and are already incorporated into HPKE itself anyway, so we shouldn't need to redo it in ECH. That leaves , which is... almost redundant. It's part of ECHConfig which is already authenticated, except in the , where it is otherwise unauthenticated. But it's also ignored in that mode so... ¯()_/¯
I've not done anything specific yet wrt HRR, and I'm guessing those details are still in-play to some extent, so not sure. But yeah, if the 2nd CH has to use the same keying then that may be the case. I agree. If there's a good reason to include it in the AAD that'd be fine, but I'm puzzled as to what that reason may be. Cheers, S.
NAME what is "it" here? The AAD? That seems orthogonal to whether or not the HPKE context (and shared KEM secret) are reused for CH2.
Sorry, yeah, that was unclear. By "it" I mean in AAD.
The reason to authenticate these things was to be as conservative as possible. Of course, we don't have any formal analysis that tells us whether or not it's strictly necessary. But given that single-shot HPKE isn't viable in this application, I'd suggest we keep it as-is. (Unless another reason to change this comes along :)
NAME given that this is needed for HRR to avoid two KEM operations, are you comfortable closing this?
(I almost missed this in today's torrent of githubbery...) Not sure. Given that HRR is rare and only needed because no good group was chosen, (and hence involves more D-H stuff in any case), I'm not clear that saving a KEM operation (in the group for ECH) is a convincing target. Is there a pointer anywhere to the analysis that such a saving is not a premature optimisation? It may be an ok design of course, but I wonder if a simpler yet less CPU efficient design might exist and be overall better. S. PS: It also strikes me that if anyone ever designs a TLS extension that contains a value (e.g. a time value) that needs to be within an RTT of something external to the h/s, then we're maybe breaking that before it's been invented.
Reusing the context is not just about performance. It also avoids a host of complexity and weird corner cases when ECH decryptions are RPC calls. It also reduces server complexity by not needing to check for the client picking a different ECHConfig the second time, etc. I would oppose regressing those complexity improvements.
Fair enough. Well, HRR+ECH seems to be it's own weirdly complex corner case as a result though. But, that may be unavoidable. I'll re-read the HRR proposal before the interim but if that requires re-tx'ing the same set of inner extension values (possibly other than URL) then that itself may be the cause of corner cases. (And break some putative yet to be defined extensions maybe.) Anyway, I think it makes sense to leave this issue open until we're a bit more settled on HRR handling. Cheers, S.
is the PR that switched it, by the way. Also some discussion in .
NAME now that is merged, which reuses the HPKE context for HRR, which accepts that HRR must be dealt with in the protocol, along with , I'd like to propose closing this. Any objections?
Reluctant acceptance here:-) S
Sounds good -- thanks!
NAME mentions possibly binding the outer CH to the ECH encryption in . If we don't do that, we should at least bind the encryption to the non-encrypted parts of the ECH extension (, , and so on). cc NAME
I also like this idea :) AD is cheap.
Closing in favor of . We can revisit the ECH extension AD information later if desired.
Re-opening to consider adding all data to AAD. NAME wrote out the flow if we did this: Make your CHInner. This should largely be running through your usual CH assembly logic. Encode it with the extensions you expect to compress. Make your CHOuter, minus the ECH extension. This should largely run through your existing CH assembly logic. Run Deserialize and SetupBaseS. Compute CHOuterAAD with half an ECH extension. Take EncodedCHInner and CHOuterAAD and run through the context.Seal logic to assemble the final CHOuter. So, not great, but also not too terrible. I'd prefer we avoid falling into the "gee, I sure wish we bound more to the encryption" state, given the history of ECH, though I agree that this isn't cheap. NAME what do you think?
To be clear, the suggestion is to change AEAD encryption so that it authenticates , , and , correct? This change seems sensible to me. One might argue that this is redundant because these parameters are incorporated into the transcript (on ECH rejection). On the other hand, ECH usage is resolved before the transcript is known! It seems like a good idea to authenticate these parameters before consuming them.
They aren't incorporated on ECH accept, which is mildly interesting. ECH accept also doesn't negotiate with them, so it's probably okay, though there is a "don't stick out" failure (depending on threat model) for the optional config_id flow if we don't authenticate them.
ESNI seems to rely on TLS 1.3 KeyShareEntry which makes it incompatible with TLS 1.2. That's unfortunate with that "The protocol designed in this document is quite straightforward." The gist of ESNI comes down to "encryptedservername extension, which contains the true extension encrypted under the provider’s public key." That seems pretty generic and backwards compatible.
To prevent cut-and-paste attacks, you need to bind ESNI to the CH. That means we’d need 1.2 clients to send a key share in the CH to use when deriving encryption keying material, and that’s not currently done.
Since TLS 1.2 sends the server certificate in plaintext it seems like ESNI wouldn't be particularly useful there anyway, no?
Indeed. I guess someone could argue about wildcard certificates or when multiple names are present but the names are usually related so I agree there’s no significant benefit from encrypting the SNI.

Discussion:
To prevent a network attacker from modifying the reconstructed ClientHelloInner (see flow-clienthello-malleability), ECH <del> authenticates ClientHelloOuter by deriving a ClientHelloOuterAAD value.  This is computed by serializing ClientHelloOuter with the "encrypted_client_hello" extension removed.  ClientHelloOuterAAD is then passed as the associated data parameter to the HPKE encryption. </del> <ins> authenticates ClientHelloOuter by computing ClientHelloOuterAAD as described below and passing it in as the associated data for HPKE sealing and opening operations.  ClientHelloOuterAAD has the following structure: The first three parameters are equal to, respectively, the "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 ClientHelloOuter with the "encrypted_client_hello" extension removed. </ins> Note the decompression process in encoding-inner forbids "encrypted_client_hello" in OuterExtensions.  This ensures the