Original text:
The "ECHConfigContents" structure contains the following fields: <del> The non-empty name of the client-facing server, i.e., the entity trusted to update the ECH configuration.  This is used to correct misconfigured clients, as described in handle-server-response. The HPKE public key used by the client to encrypt ClientHelloInner. The HPKE KEM identifier corresponding to "public_key".  Clients MUST ignore any "ECHConfig" structure with a key using a KEM they do not support. The list of HPKE KDF and AEAD identifier pairs clients can use for encrypting ClientHelloInner. </del>  The longest name of a backend server, if known.  If this value is not known it can be set to zero, in which case clients SHOULD use removed from the anonymity set during the lifetime of a particular ECH configuration.  A list of extensions that the client must take into consideration when generating a ClientHello message.  These are described below (config-extensions).  The client-facing server advertises a sequence of ECH configurations to clients, serialized as follows. When offered by the client, the extension appears only in the ClientHelloOuter.  The payload MUST have the following structure:  The cipher suite used to encrypt ClientHelloInner.  This MUST match a value provided in the corresponding "ECHConfigContents.cipher_suites" list. <del> The configuration identifier, equal to "Expand(Extract("", config), "tls ech config id", 8)", unless it is optional for an application; see optional-configs. "config" is the "ECHConfig" structure.  "Extract" and "Expand" are as specified by the cipher suite KDF.  (Passing the literal "" as the salt is interpreted by "Extract" as no salt being provided.) </del> The HPKE encapsulated key, used by servers to decrypt the corresponding "payload" field. The value of the "encrypted_client_hello" extension in the ClientHelloOuter is a "ClientECH" with the following values: <del> "cipher_suite", the client's chosen cipher suite; </del>  <del> "config_id", the identifier of the chosen ECHConfig structure; </del>  "enc", as computed above; and "payload", as computed above. If optional configuration identifiers (see optional-configs)) are <del> used, the "config_id" field MAY be empty or randomly generated. </del>  Unless specified by the application using (D)TLS or externally configured on both sides, implementations MUST compute the field as specified in encrypted-client-hello. The client then modifies the "encrypted_client_hello" extension in ClientHelloOuter as follows:  "cipher_suite" is unchanged and contains the client's chosen HPKE cipher suite. <del> "config_id" is replaced with the empty string. </del> "enc" is replaced with the empty string. "payload" is replaced with the value computed above. RFC8701 "encrypted_client_hello" extension in the first ClientHello as follows: <del> Set the "cipher_suite" field to a supported ECHCipherSuite.  The selection SHOULD vary to exercise all supported configurations, but MAY be held constant for successive connections to the same server in the same session. </del>  <del> Set the "config_id" field to a randomly-generated 8-byte string. </del>  Set the "enc" field to a randomly-generated valid encapsulated public key output by the HPKE KEM. Collect all known ECHConfigs as candidates, with trial decryption below determining the final selection. <del> Some uses of ECH, such as local discovery mode, may omit the </del>  ClientECH.config_id since it can be used as a tracking vector.  In such cases, the second method should be used for matching ClientECH to known ECHConfig.  See optional-configs.  Unless specified by the Optional configuration identifiers may be useful in scenarios where clients and client-facing servers do not want to reveal information about the client-facing server in the "encrypted_client_hello" <del> extension.  In such settings, clients send either an empty config_id or a randomly generated config_id in the ClientECH.  (The precise implementation choice for this mechanism is out of scope for this document.)  Servers in these settings must perform trial decryption since they cannot identify the client's chosen ECH key using the config_id value.  As a result, support for optional configuration identifiers may exacerbate DoS attacks.  Specifically, an adversary may send malicious ClientHello messages, i.e., those which will not decrypt with any known ECH key, in order to force wasteful decryption.  Servers that support this feature should, for example, implement some form of rate limiting mechanism to limit the damage caused by such attacks. </del>  10.5.

Reviewer comments:
We may want to change how we deal with GREASE (does a randomly selected byte stick out?), but this should address the issue for now. I think we can (should) treat these issues separately, and focus just on the simplification here. cc NAME NAME NAME NAME
My intuition is that one byte is too short; I think 32 bits would be wiser. However, this is a slightly less serious problem than usual because we can rev the ECHConfig version number and the ECH extension if we find something longer is needed.
Yep, that sounds reasonable to me.
This is somewhat of a bike shed, but 8 bytes seems quite large for something that will only have a handful of possible values at any given point in time. Is there a reason to make this longer beyond the performance cost of decryption failing as a result of collisions?
I guess the only reason I can see is that we've settled on q^2 / 2^64 as our threshold for operational risks. In particular, the "acceptance" signal in the URL is 8 bytes (URL). I suppose we could get away with a smaller identifier, since the q is much smaller in this case.
This seems different from acceptance signal collision, which actually causes breakage. In contrast, a key_id collision here just means that the server wastes cycles attempting decryption, right?
A config collision is bad because the server has to figure out which config the client used. It can only do so by trial decryption.
Eh? If there's a collision, the server knows which key to try (the one it collided with!), and it will do so, fail to decrypt, and move on as per usual. (There's no trial decryption here, but maybe I'm missing something.)
Just to put a fine point on this: the spec SHOULD NOT require that implementations support trial decryption. In particular, we ought to ensure that, with high probability, configuration identifiers don't collide. A one-byte, randomly-generated identifier would make collisions inevitable, meaning that implementations will have to support trial decryption.
Yeah, that's reasonable. If we're going to abide by this, then the ID needs to be at least 8 bytes. I'd be fine with that that for now assuming others are too. (NAME suggested it be longer at the top anyway, so many we should just do it.)
NAME we're now at 8 bytes :-)
I don't think one-byte necessary collides. First, note that the number of configs a server needs to have at a time is relatively small. Past the config's DNS TTL, you can retire it. That means you only need O(dnsttl / timebetweenrotation) configs at once. (From , I think the exact value is 1 + ceil(dnsttl / timebetweenrotation). Maybe add a little bit of leeway for people using stale DNS caches, though note we have a recovery flow so you only need to cover most, not all, of stale caches.) This means we don't need to worry about that many values. Additionally, everything here is server-allocated. Whether you're worried about a birthday bound depends on how you allocate IDs. If it's random, you have a birthday bound, yes. If you cycle through a counter, you get the full range. (Compare how QUIC and DTLS 1.3 only send the low-order bits of the epoch.) If it's random, but you check for duplicates at key issuance, you also get the full range. The counters or duplicate checks are trickier to issue, of course, though you can implement the trial decryption as a safety net. As of , the trial decryption and config_id modes are already unified. Specifically, decryption failure falls back to the outer ClientHello.
I agree with everything you said, but want to drill in on this: This seems to be key. I agree that the fallback case is clear and well understood, though one consideration is the case wherein a client attempts GREASE to a server that actually supports ECH. The probability of collision in that scenario is high, and if that scenario happens often, then decryption failure and fallback to the outer CH happens quite regularly. Bumping the length back to eight bytes removes the collision probability for that scenario, I think. I'd like to propose we treat the two problems here separately. That is, let's first see if we can land the key ID change (from a client-computed hash to a server-selected ID). And then let's see if we can safely make that ID smaller in size. I don't think we need to address both here. Would that work?
I think that the only thing that pushes toward a larger identifier space for me is the risk of grease colliding with an active key. I agree with David, that your likely number of keys is ~2 (I might go as high as 4 if the server is in an extended period of offering two types of KEM). With a one-byte identifier, that's only 1/64 greased sessions hitting the expensive decryption code. Flexibility here seems appealing, but I'm not sure that this is a place that needs it. Operationally, this seems very much dependent on centralization, so I'm not buying any story based on loose coordination. I also note that very small spaces reduce the options servers have for targeted tracking through the use of keys (though it is impossible to eliminate tracking, small spaces make the cost higher to operate tracking at any reasonable scale). That is why I was motivated to go as low as 1 byte. If you prefer a much lower probability of having to deal with failed decryption in response to greasing, then 2 bytes gets that down to lower than your likely failed connection rate at 1/16k.
Indeed, this is why I propose we separate these issues. Specifically, let's use this PR to land the server-chosen key ID, and then make it smaller (as needed) in a followup change. (Personally, I'm fine with one, two, or four bytes here, as I think the decryption failure case is unavoidable.) I'll file an issue for the size of the ID in a bit. NAME NAME NAME NAME what say you? :-)
Ditto.
After very useful discussion with many folks, it seems now is the time to remove config_id. There are a couple reasons to do this: Servers need to know the correct hash function to use when computing the ID, and possibly need to compute multiple IDs for a given public key (since a config can have multiple hash-based KDFs), which is somewhat annoying and redundant. The entire ECHConfig is already authenticated, so using a hash to prove that the client made a determination based on the correct config contents is unnecessary. Servers will likely only ever have a small handful of keys in motion at any point in time, so anything more than a couple of bits to identify one of said keys is excessive. ... there are probably more. The original design . (Oops!) It seems we're now in a better position to bring this back, but with some simplifications.
Hiya, Can you send a pointer to those discussions? (Sorry, I seem to have missed 'em;-) Ta, S.
Sorry -- these were informal conversations off-list.
Ah. That's a recurring problem. And even this exchange is actually off-list. Can we please fix that? I think I've whined about that a few times now, and would hope that continual whining is not a requirement;-) S.
few times now, and would hope that continual whining is not a requirement;-) Fix what? A few of us had a private conversation about some ideas and came to the conclusion that it would be better to replace the hash with a key_id. Then Chris and Martin brought it to a Github issue. This is precisely in line with the WG consensus to use Github issue discussion mode, as documented in 8874 S 5.3.
I'm guessing that was a private discussion amongst what could well be described as the usual suspects or an old-boys club (or are a few of you "many folks?"). Correct me if I'm wrong but that had zero visibility and zero traceability and zero accountability for anyone who isn't one of those old boys. And yet we're zapping a field in a PDU from what was declared as an "implementation draft" a day or so after one major deployment declared they had an interop target. To me, that smells of disrespect for outsiders tbh. You can probably guess what I think about that state of affairs:-) I don't buy that tbh. Chris' mail was presented as a conclusion, from a wg-chair following a private discussion that for sure excluded some implementers. (Me being one, albeit a not very good one:-) And you yourself are an ex WG-chair so I'd guess you ought also care about exclusivity here. What gain is there from that kind of behaviour? I don't see it myself. IMO, there is no need nor benefit from being so exclusive wrt this spec, yet that is what is happening and has been for a few years. I'd encourage those of you who may be "old-boys" to consider whether or not that is really the best way to proceed. IMO - it is not, you lose way more than you might think you gain, almost all the time. Cheers, S. PS: I don't disagree with the outcome, though have opinions on what -10 might contain. I do very much dislike the awful process, which is my point here.
NAME I would completely agree with you had the proposed PR been merged and the associated issue been closed. That has not happened. Further, it appears that you might be reading a bit more into Chris' issue summary than is really there. He is not acting in his WG chair capacity here because he is an author; I know he knows that and I really hope you can give him the benefit of the doubt here so that we do not have to suffer the "no WG chair hat on" in every message/post. The process, as I see it, is pretty darn near normal. Somebody files an issue and optionally (but preferably) files a PR to address said issue. Discussion ensues amd if substantive it's taken to the list. The PR may or may not be modified based on that discussion. If consensus is reached, the PR is merged and the issue is closed. We are at the discussion point. Can we do that now?
Perhaps it'd be better to not start a mail with "After very useful discussion with many folks, it seems now is the time to remove config_id." in cases where that discussion was purely private? I can understand it being hard to keep track with the various tools, but for me, that still reads as a decision being announced even if that was not the intent. Sorry for being over-sensitive to this but IMO there is a history and ongoing tendency with this spec for a subset of people to act as if they and not the IETF/WG have change control. I fully accept that they are not deliberately aiming to avoid having to garner WG consensus but consider that ongoing instances of carelessness such as the above demonstrate that there is an insider/outsider thing going on here. S.
Happy to have the identifier discussion separately. It is unfortunately not important or clear enough to lead to easy agreement. I can live with any outcome.One little nit then -> LGTM.

Revised text:
The "ECHConfigContents" structure contains the following fields:  <ins> A "HpkeKeyConfig" structure carrying the configuration information associated with the HPKE public key.  Note that this structure contains the "config_id" field, which applies to the entire ECHConfigContents.  Sites MUST NOT publish two differet"ECHConfigContents" values with the same "HpkeKeyConfig" value. </ins> The longest name of a backend server, if known.  If this value is not known it can be set to zero, in which case clients SHOULD use removed from the anonymity set during the lifetime of a particular ECH configuration. <ins> The non-empty name of the client-facing server, i.e., the entity trusted to update the ECH configuration.  This is used to correct misconfigured clients, as described in handle-server-response. </ins> A list of extensions that the client must take into consideration when generating a ClientHello message.  These are described below (config-extensions). <ins> The "HpkeKeyConfig" structure contains the following fields: An opaque identifier for the given HPKE key configuration.  This is used by clients to indicate the key used for ClientHello encryption. The HPKE KEM identifier corresponding to "public_key".  Clients MUST ignore any "ECHConfig" structure with a key using a KEM they do not support. The HPKE public key used by the client to encrypt ClientHelloInner. The list of HPKE KDF and AEAD identifier pairs clients can use for encrypting ClientHelloInner. </ins> The client-facing server advertises a sequence of ECH configurations to clients, serialized as follows. When offered by the client, the extension appears only in the ClientHelloOuter.  The payload MUST have the following structure: <ins> The ECHConfigContents.key_config.config_id for the chosen ECHConfig. </ins> The cipher suite used to encrypt ClientHelloInner.  This MUST match a value provided in the corresponding "ECHConfigContents.cipher_suites" list.  The HPKE encapsulated key, used by servers to decrypt the corresponding "payload" field. The value of the "encrypted_client_hello" extension in the ClientHelloOuter is a "ClientECH" with the following values:  <ins> "config_id", the identifier corresponding to the chosen ECHConfig structure; </ins>  <ins> "cipher_suite", the client's chosen cipher suite; </ins> "enc", as computed above; and "payload", as computed above. If optional configuration identifiers (see optional-configs)) are  <ins> used, "config_id" SHOULD be set to a randomly generated eight bytes. </ins> Unless specified by the application using (D)TLS or externally configured on both sides, implementations MUST compute the field as specified in encrypted-client-hello. The client then modifies the "encrypted_client_hello" extension in ClientHelloOuter as follows: <ins> "config_id" is replaced with the empty string. </ins> "cipher_suite" is unchanged and contains the client's chosen HPKE cipher suite.  "enc" is replaced with the empty string. "payload" is replaced with the value computed above. RFC8701 "encrypted_client_hello" extension in the first ClientHello as follows:  <ins> Set the "config_id" field to a random eight byte string. </ins>  <ins> Set the "cipher_suite" field to a supported HpkeSymmetricCipherSuite.  The selection SHOULD vary to exercise all supported configurations, but MAY be held constant for successive connections to the same server in the same session. </ins> Set the "enc" field to a randomly-generated valid encapsulated public key output by the HPKE KEM. Collect all known ECHConfigs as candidates, with trial decryption below determining the final selection.  <ins> Some uses of ECH, such as local discovery mode, may randomize the </ins> ClientECH.config_id since it can be used as a tracking vector.  In such cases, the second method should be used for matching ClientECH to known ECHConfig.  See optional-configs.  Unless specified by the Optional configuration identifiers may be useful in scenarios where clients and client-facing servers do not want to reveal information about the client-facing server in the "encrypted_client_hello"  <ins> extension.  In such settings, clients send a randomly generated config_id in the ClientECH.  Servers in these settings must perform trial decryption since they cannot identify the client's chosen ECH key using the config_id value.  As a result, support for optional configuration identifiers may exacerbate DoS attacks.  Specifically, an adversary may send malicious ClientHello messages, i.e., those which will not decrypt with any known ECH key, in order to force wasteful decryption.  Servers that support this feature should, for example, implement some form of rate limiting mechanism to limit the damage caused by such attacks. </ins> 10.5.