Change:
, this change mitigates an active "don't stick out" attack . The attack is as follows. The attacker intercepts the client's first flow () and forwards it to the server. The attacker intercepts the server's first flow and transforms and into and respectively, as follows. is just like except that the key share is replaced with key share , where is known to the attacker. is a malformed handshake message encrypted using a key derived from . Finally, it sends to the client. If the client aborts with a "badrecord_mac" alert, then the attacker guesses that real-ECH was used. Otherwise, it guesses that grease-ECH was used.
I have really mixed feelings about this. On the one hand, it's kind of annoying extra complexity. OTOH, given how hard a time we have had by reasoning about the security of ECH, I think better safe than sorry.
NAME can you please rebase this PR?
NAME this is nice -- thanks! I think the new type name for the special SH is fine, though if someone has an idea for a better name, let's consider it.
This change is definitely more invasive than the existing signal, but not all that hard to implement. (E.g., URL) cc/ NAME NAME
Thanks, NAME NAME and NAME if you can also comment on the implementation difficulty, we can try and land this soon.
This seems pretty straightforward. I don't think it would be too difficult to do in NSS.
I think on balance we should do this.
The primary "don't stick out" [RFC8744] consideration for ECH is to ensure that the new extension does not trigger middlebox misbehavior. To accomplish this, the draft specifies two protocol variants: in the "real-ECH" protocol the client offers the ECH extension, which is processed by the server; in the "grease-ECH" protocol, the client generates a dummy ECH extension, which is ignored or rejected by the server. Grease-ECH is designed to look enough like real-ECH so that middleboxes do not differentiate the latter from the former, the idea being that if grease-ECH can be deployed without triggering middlebox misbehavior, then real-ECH ought to be deployable, too. But what do we mean when we say grease-ECH should "look like" real-ECH? In ECH-08 the stated goal is "to hide, to some extent, (a) whether or not a specific server supports ECH and (b) whether or not ECH was accepted for a particular connection" ]. These goals are hard to meet. First, an attacker can violate (a) by simply running grease-ECH with the server to see if it returns retry configs. Second, there are a number of ways to distinguish real-ECH from grease-ECH, thereby violating (b): NAME . If the ECH config is known to the attacker, then it can use ClientECH.configid to distinguish real-ECH from grease-ECH. The outer SNI must match ECHConfig.publicname. Hence, for connections to a given backend server, the outer SNI will be different in real-ECH than in grease-ECH. The client and server must negotiate TLS 1.3 when ECH is accepted, but may otherwise negotiate an earlier version. In particular, it is valid for a client to run grease-ECH but not indicate support for 1.3. PSK must not be offered in the outer CH in real-ECH, but may be offered in grease-ECH. In general, the set of extensions negotiated may depend on whether ECH was accepted. The client-facing server must not request client authentication when rejecting ECH, but the backend server may request client authentication when ECH was accepted. Whether or not an HRR message is issued by the server may depend on ECH acceptance. There are a few active distinguisher attacks that exploit the HRR codepath. Some of these distinguishers can be mitigated by careful implementation, while mitigating others would require changes to the specification. (E.g., .) However, it's unlikely that all implementations will mitigate all of these distinguishers. Moreover, future extensions are likely to change the shape of TLS in a way that leads to yet more distinguishers. Given all of this, I wonder if indistinguishability (of real-ECH from grease-ECH) is the right way to think about this problem. It seems to me that it's stronger than strictly necessary, since the existence of a distinguisher does not necessarily imply that some feature of real-ECH will trigger middlebox misbehavior that would not otherwise be triggered by grease-ECH. For example, while the outer SNI might be a dead give away of likely ECH usage, this value is unlikely to impact middlebox behavior. Similarly, the absence of the PSK extension in real-ECH is unlikely to cause issues. If it were possible to enumerate the set of properties that impact middlebox behavior, then ensuring that middleboxes don't differentiate between real-ECH and grease-ECH may simply be a matter of ensuring that this set of properties does not vary. This approach would be roughly analogous to the design of "Middlebox Compatibility Mode" in TLS 1.3, which makes the handshake "look like" a particular mode of 1.2 that middleboxes are known to tolerate. Of course, a curious adversary (e.g., a censor) can easily distinguish between these, but experience suggests that middleboxes do not treat them differently. This leaves us the daunting task of deciding what features are known to impact the behavior of existing middleboxes. Here's a preliminary list of things that might fit in this category. For the sake of discussion, let's call them differentiable features: The set of extensions that appear to be negotiated; Visible alerts, e.g., "badrecordmac"; Number of round trips in a given transport context (e.g., TCP connection); Packet size, or the number of packets comprising a single message; Timing; ... I think we should set the following goals for ECH-09 (or some draft that follows it). If we can agree on a set of differentiable features, then we should ensure that this set does not vary between real-ECH and grease-ECH. ECH-08 aims only to mitigate passive "don't stick out" attacks, but there is a lot of evidence that middleboxes exhibit active behavior. (For example, middleboxes have been observed removing CH extensions they don't recognize or adding their own.) Thus, we should aim to thwart attacks that change the set of differentiable features in real-ECH vs. grease-ECH. This includes NAME attack in particular: see . To the greatest extent possible, the spec should allow for implementations to aim for the strongest possible notion of security. For example, we might consider relaxing the requirement that the outer SNI matches ECHConfig.public_name, so long as this change doesn't impact interoperability.
For the PSK extension specifically, I think we have two distinguishers to consider: GREASE ECH + PSK extension in a Client Hello identifies GREASE ECH, due to PSKs being disallowed in CHOuter. Real ECH + resumption (or any accepted PSK) identifies accepted ECH. CHOuter contains no PSK extension, but SH will contain the presharedkey extension (selected_identity).
Due to assertions stated in IETF meetings that ECH isn't an anti-censorship tool, and the literally innumerable difficulties in making real ECH indistinguishable from GREASE ECH (as mentioned above), I propose dropping the "do not stick out" goal. The primary aim of ECH is to make the SNI private, and it's possible to keep the SNI private while telegraphing that we're using ECH. We can, however, still use GREASE to make sure middleboxes are prepared to handle the code point and the larger size of the ClientHello.
I agree with cjpatton's analysis. I don't think anyone is asking that these things be "indistinguishable" to a determined active intermediary in the common case. That is clearly impossible (e.g. cjpatton's attack 2). Instead, the goal is to minimize barriers to deployment of ECH. If ECH isn't reliable in practice, it won't be widely deployed, and so will not keep the SNI private. In my view, the minimum threshold is: a passive intermediary with only per-connection state must not be able to distinguish ECH from GREASE with confidence for a typical client and server configuration. Defenses for less common protocol uses and low-grade active intermediaries (e.g. simple replay) are good to have but we shouldn't delay the protocol if they prove difficult. I think the only remaining issue to reach this level is Real ECH + resumption (kjacobs second attack). All the other attacks (a) only affect a subset of GREASE (e.g. resumption GREASE), (b) require the intermediary to be active (emitting or modifying packets), or (c) require the intermediary to have per-server state (e.g. a DNS database or a persistent server profile). We should build defenses for those cases too where we can, but some are intractable, and that's OK.
It is kind of sad, but I agree with the analysis. The sticking out starts with the definition of ECH as an extension, which already provides a differentiation from standard TLS. The theory that grease will fix that by making it hard to distinguish "real" and "fake" ECH is most probably wrong, except if the middlebox is really naive. That means the only sure way to grease the ECH function is to actually use it, even when the inner SNI is the same as the outer ECH -- only a real ECH is undistinguishable from a real ECH.
I agree with NAME The ECH goals in the document mostly align with this proposed minimum threshold. I think we should close this with no action, or tweak language to make this more clear, assuming we have consensus. NAME NAME NAME NAME what do you think?
I don't think action is needed at this point, but I think we should keep the issue open until we're closer to RFC. We're pretty close to interop, and we should expect real-world experiments to follow shortly thereafter. Those experiments will reveal if we missed anything, and it will be useful to have this issue as a reference.
This seems like a reasonable objective for avoiding operational/ossification issues.
NAME description sounds right. I would note that this seems to actually make unnecessary, right? Not saying we necessarily need to not do it, but...
Just to see about sharpening NAME description: if an attacker needs to interfere with a connection in such a way that it would cause a failure in that connection in order to determine whether ECH is in use, that's out of scope, right?
Yes, that seems like a higher threshold than the one I proposed (purely passive). I'm not sure about "out of scope". For example, I fully support . I think that when we run into problems like this, we should solve them if we have a solution available, but not delay the protocol if we have to go searching for one.
I disagree. I think that if a particular threat model is out of scope, it's out of scope, and any mitigations of them shouldn't be in the document. There's no point in half-measures that ultimately won't be sufficient for addressing certain types of attackers but will add complexity.
An interesting detail on this one: interfering with the bytes and observing either client or server's response will break that connection, but observing the server's response is a little different from observing the client. One can often make a second connection to the server. (Perhaps a "passive and a half" attack.) That is, suppose the server sent decryptfailure on bad ECH decryptions. (It no longer does.) You can determine whether ECH is in use by flipping a bit in the ciphertext and seeing if the server notices. That breaks the connection, but you could make a second connection to the server, send your perturbed ClientHello, and not interfere with the original connection. Whereas, if your attack involves probing the client's behavior, you'd need to induce the client to start a second connection and not notice the first. Systems do tend to retry on failure, so this isn't entirely_ implausible, but harder than the first attack.
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. The ECH proposal for Encrypted SNI is almost ready, but for a very small debate. The original proposal was using trial description to distinguish between ECH aware responses to the encrypted inner Client-Hello from non ECH aware response to the "cover" outer CH. This is problematic in the QUIC use case. The latest proposal is to embed a "hint" in 8 bytes of the Server Random. The proposal was for ECH-aware servers to use eight bytes of a hash of the inner Client Random as a hint. Analysis shows that this enables two attacks: 1) If the Client Hello is replayed, the same hint will be present in the response to the original CH and to the response to the copy, providing observers with a clear indication that ECH was used. 2) If the Client Hello is intercepted by an MITM attacker, the attacker can rewrite the server's response before presenting it to the client. The attacker formats its own Server Hello that reuses the Server Random from the original server response, but use its own key share, etc. The hint will cause the ECH aware client to create an handshake key using the inner CH. In a QUIC set up, the MITM attacker can easily detect that, before even transmitting the server's certificate. Thus, the MITM attacker can detect usage of ECH. We have a simple proposal that solves the replay attack: set the hint as a hash of both the inner Client Random and the "non-hint" bytes of the Server Random. That's clearly a good idea, but it does not solve the active MITM attack. Solving that requires tying the hint with the handshake key derived by the original server, for example by hashing the inner Client Random, the "non-hint" bytes of the Server Random, and the server key share. That's harder to implement, so the question is about cost and opportunity. This all relates to how much ECH is "sticking out". The current stance is that a passive attacker cannot distinguish between a client using ECH to access a hidden SNI and a client merely greasing the ECH extension. The observation is that there may be many potential active attacks, especially if the server shares its ESNI/ECH configuration publicly. If there are many such attacks, and if defense of the hint against MITM is hard to implement, implementing the defense might make the code more fragile, with little actual gain. But I am not convinced by that argument, because it smells a lot like "the other side of the boat is leaking too, why should I plug this particular leak?" And so, at Chris Wood's request, I am sending this message to the list. -- Christian Huitema
Ok, in that case it is worth making the don’t “stick out” property more precise. 1) We can either try to prevent the attacker from learning whether ECH was actually used in a particular connection, or 2) We can try to prevent the attacker from learning whether the server supports ECH at all. I gather the consensus is that we should try to obtain (2) at least until ECH support becomes ubiquitous on the server side? Since TLS 1.3 moves all but a few SH extensions to EE, I suppose there isn’t any other extension to hide this signal in. In that case, I guess using a value derived from the handshake key as part of the ServerRandom should probably work. That is, when constructing the ServerHello: first set the first N bytes of the server random to 0 compute the hanshake secret (based on either the inner or outer CH) compute the signal of N bytes as signal = HKDF-Expand-Label(handshake secret, “s hs hmac”, Transcript-Hash(ClientHello..ServerHello), N) replace the first N bytes of the server random with signal This is a somewhat icky design, but I think we should be able to justify it for (say) N <= 16 bytes.

Discussion:
use ClientHelloInner or reject it and use ClientHelloOuter.  In handling the server's response, the client's first step is to determine which value was used.  The client presumes acceptance if <del> the last 8 bytes of ServerHello.random are equal to "accept_confirmation" as defined in backend-server.  Otherwise, it </del> <ins> the last 8 bytes of ServerHello.random are equal to the first 8 bytes of "accept_confirmation" as defined in backend-server.  Otherwise, it </ins> presumes rejection. 6.1.2.1.

Upon receipt of an empty "encrypted_client_hello" extension, if the backend server negotiates TLS 1.3 or higher, then it MUST confirm ECH <del> acceptance by setting ServerHello.random[24:32] to where HKDF-Expand-Label and HKDF-Extract are as defined in RFC8446. The value of ServerHello.random[0:24] is generated as usual by invoking a secure random number generator (see RFC8446, Section 4.1.2). </del> <ins> acceptance to the client by computing its ServerHello as described here. The backend server begins by generating a message ServerHelloECHConf, which is identical in content to a ServerHello message with the exception that ServerHelloECHConf.random is equal to 24 random bytes followed by 8 zero bytes.  It then computes a string where Derive-Secret and Handshake Secret are as specified in RFC8446, Section 7.1 and ClientHelloInner..ServerHelloECHConf refers to the sequence of handshake messages beginning with the first ClientHello and ending with ServerHelloECHConf.  Finally, the backend server constructs its ServerHello message so that it is equal to ServerHelloECHConf but with the last 8 bytes of ServerHello.random set to the first 8 bytes of accept_confirmation. </ins> The backend server MUST NOT perform this operation if it negotiated TLS 1.2 or below.  Note that doing so would overwrite the downgrade