jiebi/CodeConvo huggingface dataset:
Code change:
When sending ClientHello, the client first computes ClientHelloInner, including any PSK binders, and then MAY substitute extensions which it knows will be duplicated in ClientHelloOuter.  To do so, the <del> client computes a hash of the entire ClientHelloInner message with the same hash as for the KDF used to encrypt ClientHelloInner.  Then, the client removes and replaces extensions from ClientHelloInner with a single "outer_extensions" extension.  The list of "outer_extensions" include those which were removed from </del> <ins> client computes a hash of the entire ClientHelloInner message as: where "inner" is the ClientHelloInner structure and "Extract", "Expand", and "Nh" are as defined by the KDF API in I-D.irtf-cfrg- hpke.  Then, the client removes and replaces extensions from ClientHelloInner with a single "outer_extensions" extension.  The list of "outer_extensions" include those which were removed from </ins> ClientHelloInner, in the order in which they were removed.  The hash contains the full ClientHelloInner hash computed above.

Related discussion:
text: Let's close this issue and leave the mechanism as is.
Hrm, before closing I think it might be worth noting, in the spec, the stronger security property this provides.
I think we should also consider binding the CHInner to a hash of the whole CHOuter (minus the ClientEncryptedCH extension). That would have the same size overhead, but would give us a stronger property: modifying anything in CHOuter (not just one of the copied extensions) invalidates the whole thing.
The point of hashing outer extensions is to provide integrity of the ClientHelloInner. What would be the point in hashing more of the ClientHelloOuter? (Not a rhetorical question :) It seems conceptually different, and I'm curious what the security benefit might be.)
If we authenticate the ClientHelloOuter, we'd be authenticating all the inputs that go into the ClientHelloInner, so I think that would also provide integrity. I expect Ben is suggesting this in the context of URL, where GREASE can additionally be made a little stronger if we did that. Getting that to interact well with any PSK binders in ClientHelloOuter is tricky though, unless we decide to ban resumption attempts in outer ClientHellos. (Probably best to keep the GREASE-related conversation in one place. Though I see I didn't help on that front because some discussion is also in . Ah, GitHub.)
Yep, I'm just echoing NAME observation. See also URL
Today, the only thing that gets copied into ClientHelloInner is extensions, so I don't see how it's an improvement in terms of our primary security goal. (Not sure about the "don't stick out" benefit.)
I don't understand how hashing all of CHOuter is going to work. In split mode, the server never sees any of CHOuter.
Yes, the hash would be checked when reconstructing the inner ClientHello (as here), and would not be present in the reconstructed ClientHello.
There's not much discussion here. Unless there's objection, shall we leave the mechanism as-is and close out this issue? cc/ NAME
Works for me.
From Section 6.1: This assumes that the KDF specifies some underlying hash function.
Code change:
When sending ClientHello, the client first computes ClientHelloInner, including any PSK binders, and then MAY substitute extensions which it knows will be duplicated in ClientHelloOuter.  To do so, the <del> client computes a hash of the entire ClientHelloInner message with the same hash as for the KDF used to encrypt ClientHelloInner.  Then, the client removes and replaces extensions from ClientHelloInner with a single "outer_extensions" extension.  The list of "outer_extensions" include those which were removed from </del> <ins> client computes a hash of the entire ClientHelloInner message as: where "inner" is the ClientHelloInner structure and "Extract", "Expand", and "Nh" are as defined by the KDF API in I-D.irtf-cfrg- hpke.  Then, the client removes and replaces extensions from ClientHelloInner with a single "outer_extensions" extension.  The list of "outer_extensions" include those which were removed from </ins> ClientHelloInner, in the order in which they were removed.  The hash contains the full ClientHelloInner hash computed above.

Related discussion:
text: From the HPKE spec, it seems like all that's required of the KDF is that it's an "extract-then-expand" KDF: URL In fact, the currently assigned code points are all for HKDF: URL But there's no requirement that the KDF always have some underlying hash function. As an alternative to HKDF, one can imagine a dedicated primitive that satisfies the extract-then-expand API but is not constructed from a hash function.
Impacted fields: ClientEncryptedCH.record_digest URL
We can probably replace with and be done with it.
Putting on my theorist hat, I'd say that the extract function is a "randomness extractor" and not a collision resistant hash function. We want the latter in this context. From a practical stand point, using the extract function is more hashing than we need. I'd suggest doing away with (see ) and figuring something else out for . Could we simply require HKDF be used with HPKE?
Sure, but in practice randomness extractors use a collision resistant hash or PRF (CBC) under the hood. I don't think we can require HKDF for HPKE. That seems too restrictive. We might just say that ECH requires HKDF-based HPKE ciphersuites, as HKDF is already paramount for TLS 1.3, and then just reference HKDF's hash function.
addresses this issue.
As I mentioned on the list, we can use the KDF in place of a hash.
I tend to agree. I'll create a new PR.
Have a look at , NAME
Resolved in . Closing.
Code change:
if they define semantics for such HTTP Datagrams and negotiate mutual support. <ins> 3.1. Implementations of HTTP/3 that support HTTP Datagrams can indicate that to their peer by sending the H3_DATAGRAM SETTINGS parameter with a value of 1.  The value of the H3_DATAGRAM SETTINGS parameter MUST be either 0 or 1.  A value of 0 indicates that HTTP Datagrams are not supported.  An endpoint that receives the H3_DATAGRAM SETTINGS parameter with a value that is neither 0 or 1 MUST terminate the connection with error H3_SETTINGS_ERROR. Endpoints MUST NOT send QUIC DATAGRAM frames until they have both sent and received the H3_DATAGRAM SETTINGS parameter with a value of 1. When clients use 0-RTT, they MAY store the value of the server's H3_DATAGRAM SETTINGS parameter.  Doing so allows the client to send QUIC DATAGRAM frames in 0-RTT packets.  When servers decide to accept 0-RTT data, they MUST send a H3_DATAGRAM SETTINGS parameter greater than or equal to the value they sent to the client in the connection where they sent them the NewSessionTicket message.  If a client stores the value of the H3_DATAGRAM SETTINGS parameter with their 0-RTT state, they MUST validate that the new value of the H3_DATAGRAM SETTINGS parameter sent by the server in the handshake is greater than or equal to the stored value; if not, the client MUST terminate the connection with error H3_SETTINGS_ERROR.  In all cases, the maximum permitted value of the H3_DATAGRAM SETTINGS parameter is 1. It is RECOMMENDED that implementations that support receiving HTTP Datagrams using QUIC always send the H3_DATAGRAM SETTINGS parameter with a value of 1, even if the application does not intend to use HTTP Datagrams.  This helps to avoid "sticking out"; see security. 3.1.1. [[RFC editor: please remove this section before publication.]] Some revisions of this draft specification use a different value (the Identifier field of a Setting in the HTTP/3 SETTINGS frame) for the H3_DATAGRAM Settings Parameter.  This allows new draft revisions to make incompatible changes.  Multiple draft versions MAY be supported by either endpoint in a connection.  Such endpoints MUST send multiple values for H3_DATAGRAM.  Once an endpoint has sent and received SETTINGS, it MUST compute the intersection of the values it has sent and received, and then it MUST select and use the most recent draft version from the intersection set.  This ensures that both endpoints negotiate the same draft version. </ins> 4. This specification introduces the Capsule Protocol.  The Capsule

Related discussion:
text: When reviewing the latest editors copy, the document structure leaps out at a bit odd by jumping from section 3 to section 5. That indicates to me these concepts are linked and also independent of the capsule protocol. So here I propose a minor restructure by moving the H3_DATAGRAM setting stuff to be a subsection of the HTTP/3 datagram format. This makes all the top-layer sections loosely coupled, which is nicer in my opinion.
Code change:
2.1.2. <ins> A dynamic table entry is considered blocking and cannot be evicted until its insertion has been acknowledged and there are no outstanding unacknowledged references to the entry.  In particular, a dynamic table entry that has never been referenced can still be blocking. A blocking entry is unrelated to a blocked stream, which is a stream that a decoder cannot decode as a result of references to entries that are not yet available.  Any encoder that uses the dynamic table has to keep track of blocked entries, whereas blocked streams are optional. </ins> An encoder MUST NOT insert an entry into the dynamic table (or <del> duplicate an existing entry) if doing so would evict an entry with unacknowledged references.  For header blocks that might rely on the newly added entry, the encoder can use a literal representation. </del> <ins> duplicate an existing entry) if doing so would evict a blocking entry.  In this case, the encoder can send literal representations of header fields. </ins> To ensure that the encoder is not prevented from adding new entries, the encoder can avoid referencing entries that are close to eviction.

Related discussion:
text: NAME Thank you for your feedback.
Thanks for this. I don't have a better name for blocking entries right now, but I will keep thinking on it as I agree it could confuse people even more.
This looks reasonable.
Code change:
For each offered m= section, if any of the following conditions are true, the corresponding m= section in the answer MUST be marked as rejected by setting the port in the m= line to zero, as indicated in <del> RFC3264, Section 6., and further processing for this m= section can be skipped: </del> <ins> RFC3264, Section 6, and further processing for this m= section can be skipped: </ins> The associated RtpTransceiver has been stopped. <del> No supported codec is present in the offer. </del> <ins> None of the offered media formats are supported and, if applicable, allowed by codec preferences. </ins> The bundle policy is "max-bundle", and this is not the first m= section or in the same bundle group as the first m= section.

Related discussion:
text: LGTM and if Justin wants to tie in the stuff from my comment a bit more that is great
NAME PTAL
seems like it's generally right,LGTM
Code change:
any of these checks fail, then the CA MUST reject the new-account request. <del> 7.3.3. </del> <ins> 7.3.6. </ins> A client may wish to change the public key that is associated with an account in order to recover from a key compromise or proactively

Related discussion:
text: This PR is a first go at addressing the following points I raised on the mailing list.
This allows us to address the account URL recovery use case in without having special handling for empty payloads.
Code change:
11.1.1. An Add proposal requests that a client with a specified KeyPackage be <del> added to the group. </del> <ins> added to the group.  The proposer of the Add MUST validate the KeyPackage in the same way as receipients are required to do below. </ins> The proposer of the Add does not control where in the group's ratchet tree the new member is added.  Instead, the sender of the Commit

Related discussion:
text: This PR adds a RequiredCapabilities extension that allows the members of a group to impose a requirement that new joiners support certain capabilities. The extension is carried in the GroupContext and verified at Add time (including on external commits). In the interim 2021-10-04, we briefly discussed whether this mechanism needed to be included in the core protocol. Because of its interactions with Add and GroupContextExtensions proposals, it seems worth having in the main protocol.
s currently have two usages in the spec: Pre-published key packages that you can download and put in a leaf when adding a new member leaf node content that you update when doing a full Currently, things have been designed to use only one structure for both uses, leading to some weirdness such as: and are useful in the usage 1, but are parameters of the group in usage 2. is a good name in usage 1, but would better be named in usage 2. some data is important in usage 2 and have no meaning in usage 1, hence they were designed as "mandatory-when-updating extensions" (such as , and we were also discussing about adding the in to help prevent cross-group attacks) How about using different structures for these? // Essentially identical to KeyPackage struct { ProtocolVersion version; CipherSuite ciphersuite; HPKEPublicKey hpkeinitkey; opaque endpointid; Credential credential; Extension extensions; opaque signature; } PrePublishedKeyPackage; struct { HPKEPublicKey hpkekey; opaque endpointid; Credential credential; opaque parenthash; opaque groupid; Extension extensions; opaque signature; } LeafNodeContent; enum { reserved(0), prepublishedkeypackage(1), leafnodecontent(2), (255) } LeafNodeType; struct LeafNode { // This is not included in the signature, but with the "unambiguous signature" PR that's not a problem LeafNodeType type; select(URL) { case prepublishedkeypackage: PrePublishedKeyPackage data; case leafnode_content: LeafNodeContent data; } } What would you think about refactoring like this?
If we're doing this, I'd suggest including a dedicated public key to encrypt Welcome messages to for the . NAME suggested this at some point.
Code change:
11.1.1. An Add proposal requests that a client with a specified KeyPackage be <del> added to the group. </del> <ins> added to the group.  The proposer of the Add MUST validate the KeyPackage in the same way as receipients are required to do below. </ins> The proposer of the Add does not control where in the group's ratchet tree the new member is added.  Instead, the sender of the Commit

Related discussion:
text: It gives us better FS guarantees, because the receiver of the can delete the corresponding private key immediately after decryption, while they might have to keep the other key around for a bit until they can actually issue an update. This might also help clarify .
Ties into as well because is an example of an extension that is required and should be a field instead
NAME one concern I have is how is that field in created?
When sending a full , the participant includes a new in the (it replaces the field). It computes the signature with where: corresponds to all the fields of except (the usual thing) comes from
What about the scenario where Alice pulls down Bob's key package to add him to the group? In that case Alice can't do the conversion between formats on Bob's behalf.
Alice don't have to do the conversion, this is why either contain a (when adding someone to the group) or a (when updating your own key package).
Ah somehow I missed that part in your original post. Makes sense
I like this general direction. It solves a few things. Instead of having leaf nodes contain either a prepublished thing or a normal leaf node, how about having the prepublished thing wrap the leaf node thing? As a bonus, we get separation between the key pair used for Welcome encryption and the key pair that goes in the leaf for TreeKEM. Then when Alice adds Bob, she extracts his LeafNodeContent and sends that in the Add. That doesn't let the other members of the group verify that Alice did the right thing w.r.t. expiry, but I'm not heartbroken about that. We might also be able to remove the extensions field from the outer (KeyPackage) wrapping if we turn the extension into KeyPackage field. I expect we probably need leaf extensions, but would be open to making into a field.
Why not keep sending the KeyPackage in the Add? That wouldn't cost much and would allow everyone to validate the lifetime. Note, that the lifetime is crucial in achieving PCS for pre-published key material. If I compromise Bob and get hold of the private key material of a bunch of his KeyPackages, I can add "Bob" to any group I want as long as the Credential inside the LeafNodeContent remains valid and Bob doesn't revoke the LeafNodeContent somehow.
Code change:
11.1.1. An Add proposal requests that a client with a specified KeyPackage be <del> added to the group. </del> <ins> added to the group.  The proposer of the Add MUST validate the KeyPackage in the same way as receipients are required to do below. </ins> The proposer of the Add does not control where in the group's ratchet tree the new member is added.  Instead, the sender of the Commit

Related discussion:
text: Also, we'll have to do this if we want to enable any member to send the Welcome message as you proposed here: URL
I agree that including inside is quite clean, but something is bothering me with this approach: I'd like that the is signed by the participant occupying that leaf, to protect against cross-group attacks. In the first message of this issue I did this by including in , but in fact we don't need to store it in the leaf, we just need to include it in the . In any case, if we want to include in the signature, we need a way to differentiate between leaves that triggered a commit and the ones that are "fresh". And for that I think using a tagged-union struct is the cleanest way to do it.
It always feels a bit lazy to use an extension (and implementations have to be careful to check for their presence), but we could use a extension in the same way that we use a extension. You will find both of them only on LeafContents that were updated at least once. Another result of including GroupId in the KeyPackage is that the signature will explicitly sign the and I'm not sure if that doesn't completely destroy any deniability guarantees we can still get from MLS.
I don't think there's really a good way to get the into the leaf. Because of prepublication, it would have to be optional, but in order to prevent attacks, you need rules to decide whether it's required in a given situation. It seems like the best you can do is basically TOFU: When you first get the tree, you can't validate whether the groupid should be in a given leaf or not Once you're a member, you require that groupid is sent in Update and UpdatePath.leafkey_package I could live with a solution of that character, but given it's a bit complex, might prefer to leave it out unless we get a useful property from it. NAME - Re: Sending KeyPackage in Add - Yep, that makes sense. So you would send KeyPackage in Add, and LeafNode in Update.
I tried to worked through what would be necessary to implement this. It looks like it would require some fairly invasive changes to be made to rationalize the document's structure before making this change. But those changes should probably be made anyway.
Code change:
11.1.1. An Add proposal requests that a client with a specified KeyPackage be <del> added to the group. </del> <ins> added to the group.  The proposer of the Add MUST validate the KeyPackage in the same way as receipients are required to do below. </ins> The proposer of the Add does not control where in the group's ratchet tree the new member is added.  Instead, the sender of the Commit

Related discussion:
text: Proposal: Preparatory PR: Rationalize layout Move "Cryptograhic Objects" to before "Ratchet Trees" Update "Ratchet Tree Nodes" to define ParentNode and LeafNode structs, with LeafNode initially just equal to KeyPackage Move subsections of "Key Packages" to under "Ratchet Trees" Parent Hash Tree Hashes Update Paths Move Group State to its own top-level section Main PR: Change LeafNode to have the required content: HPKE key for TreeKEM Credential Parent hash Capabilities Extensions Signature Change KeyPackage to refer to LeafNode for internals, plus: HPKE key for Welcome Lifetime Extensions Signature Add continues to send KeyPackage Update and Commit send LeafNode instead of KeyPackage Remove and Sender refer to LeafNodeRef instead of KeyPackageRef We should probably do the preparatory PR regardless of this issue.
Discussion on working call: General agreement on the approach Should sign group_id when possible Include in TBS if we at least have a flag for pre-published / in-group Or we can store it Or we could have independent structs for pre-published / in-group leaves Do we need extensions at both levels? Capabilities / lifetime should probably be promoted to fields
Many protocols which have an extensibility mechanism have a way to indicate that understanding an extension is mandatory for certain behaviors. For MLS, this would mean having a way to require a member understand an MLS extension before it can join a group. The three extension types defined currently are: MLS ciphersuites, MLS extension types, and MLS credential types. One logical way to add mandatory extensions would be to pick one of these types (the MLS extensions type) and use the high order bit or a specific range to indicate that a member must understand and implement the extension to join a group which uses that extension. The IANA values could be changed as follows: Values from 0x8000-0xffff indicate understanding the extension is required 0x7f00 - 0x7fff Reserved for Private Use (not required) 0xff00 - 0xfff Reserved for Private Use (required) If a required ciphersuite or credential becomes necessary, a new extension type could be created to add an extension in the KeyPackage or GroupInfo which would be required and would indicate support for the mandatory ciphersuites or credentials.
Code change:
11.1.1. An Add proposal requests that a client with a specified KeyPackage be <del> added to the group. </del> <ins> added to the group.  The proposer of the Add MUST validate the KeyPackage in the same way as receipients are required to do below. </ins> The proposer of the Add does not control where in the group's ratchet tree the new member is added.  Instead, the sender of the Commit

Related discussion:
text: In order to prevent some other entity from adding an MLS client that does not support a mandatory extension, we already have the KeyPackage Client Capabilities, which lists which extensions are supported by the client.
Note that though this might seem "now or never", I don't think it really is. Consider TLS by way of analogy -- it has no mechanism for specifying that extensions are required. But if a server gets a ClientHello that is missing some extension that it requires, it can just abort the handshake. Likewise in MLS, the member adding a new joiner to the group can examine that joiner's KeyPackage to see if they support the extensions needed for the group. So I think you could implement this as a non-mandatory GroupContext extension, which would state the extensions required by the group so that all members apply the same requirements for new joiners.
While you can implement this "out-of-band", that makes it implicit rather than explicit behavior.
Discussed at 20211004 interim, liked this functionality. But, instead of IANA registry use field (or extension) in group context to indicate which extensions are required. NAME to generate PR.
Discussed with NAME and incorporated this functionality into PR