15.2.  MLS does not provide confidentiality protection to some messages and fields within messages: as a guide to understanding how they might be exposed or protected in a given application. <del> 15.2.1. </del>  MLS provides no mechanism to protect the group ID and epoch of a message from the DS, so the group ID and the frequency of messages and epoch changes are not protected against inspection by the DS. <del> 15.2.2. </del>  A group's extensions are first set by the group's creator and then updated by GroupContextExtensions proposals.  A GroupContextExtension confidential to the group and the new joiner, then it will leak the group's extensions. <del> 15.2.3. </del>  The group's membership is represented directly by its ratchet tree, since each member's LeafNode contains members' cryptographic keys, a node to be less identifying, e.g., by using a pseudonymous credential and frequently rotating encryption and signature keys. <del> 15.3. </del>  The first form of authentication we provide is that group members can verify a message originated from one of the members of the group. that ensures that the legitimate members of the group have the required PSKs. <del> 15.4. </del>  Forward secrecy and post-compromise security are important security notions for long-lived MLS groups.  Forward secrecy means that This way, the key material used to add a member to a new group is more likely to be fresh and less likely to be compromised. <del> 15.5. </del>  KeyPackages are intended to be used only once.  That is, once a KeyPackage has been used to introduce the corresponding client to a KeyPackage publication system SHOULD rate-limit KeyPackage requests, especially if not authenticated. <del> 15.6. </del>  It is possible for a malicious member of a group to "fragment" the group by crafting an invalid UpdatePath.  Recall that an UpdatePath "SHA384", and "SHA512" refer to the SHA-256, SHA-384, and SHA-512 functions defined in SHS.  It is advisable to keep the number of ciphersuites low to increase the chances clients can interoperate in a federated environment, therefore the ciphersuites only include modern, yet well-established

I would propose we say that we need AE1 security for our AEAD scheme, which gives us random ciphertexts and if we have random ciphertexts the input to key/nonce generation for sender data encryption is unique. This in turn gives us AE1 security for sender data encryption. I don't think we need AE2 encryption because our nonces are random and don't reveal any private information. Also, if I understand correctly, AES GCM is not AE2 secure, so if we want AE2 security, we need a different scheme or the transform described in NAN. Probably needless to say, but I don't have a security proof for any of this. So more eyes on whatever we write here would be appreciated.
Comment by NAME I don't think I understand the security model here? What happens if the previous ciphertext has 00's for the first KDF.Nh bytes?
The ciphertext sample here is just acting as a salt to produce several distinct key/nonce pairs from a single . So as with , all-zeros isn't inherently harmful here, unless it happens repeatedly. (Note that this scheme was inspired by , except the latter only uses 16 bytes of sample and XOR instead of AEAD.) If the content encryption is done properly, then the probability of any particular value is vanishingly small, due to the ciphertext looking like random data. Someone could construct a ciphertext that results in nonce reuse, e.g., having the same bytes as a previous message. However, note that the harm here is done when the reused key/nonce is used to encrypt and the encrypted sender info is published to the attacker. This means that the attack cannot be mounted from outside the group. Even if the attacker can cause a sender to encrypt a given plaintext, the attacker would not know the key with which it would be encrypted. So the best attack is for an insider to the group to leak the sender info. cc NAME who did some analysis on this IIRC
I didn't do analysis on this, however I'm a bit concerned by the assertion "due to the ciphertext looking like random data". I don't think this is a standard security assumption we do on AEADs? For example if I take a secure AEAD, and build a new one by prefixing zeroes, the new AEAD is as secure as the original one? To make sure that there is no key/nonce re-use, I think it would be better to use a full hash on the ciphertext, but it increases computation cost.
Yeah, I'm also a bit wary of this assumption. I think this would correspond to -IND-CCA security for the AEAD scheme. Wasn't this inspired by QUIC somehow (maybe header encryption)? In any case, if we don't do so already, we might want to explicitly note this in the ciphersuite section to ensure that AEAD schemes added in the future indeed produce pseudorandom ciphertexts.
It also occurs to me that is only 12 bytes long (and 12 < 16), so we could change this to literally do what QUIC does: (And there's a parallel for ChaCha) That would more clearly benefit from any QUIC analysis, and would save an extra auth tag. It would be a minor pain to implement, because you would need access to a raw cipher as well as an AEAD mode.
It looks like it also relies on the ciphertext prefix being random? I dislike this approach which is too low-level in my opinion, I would prefer staying with high-level cryptographic primitives.
I filed to try to address this. Basically, I argue that the same arguments from Bellare et al. that apply to QUIC header protection also apply here. The PR also adds a requirement that AEADs act as PRFs, as discussed here.

15.2. <ins> The MLSCiphertext framing encrypts "sender data" that identifies which group member sent an encrypted message, as described in sender- data-encryption.  As with the QUIC header protection scheme RFC9001, Section 5.4, this scheme is a variant of the HN1 construction analyzed in NAN.  A sample of the ciphertext is combined with a "sender_data_secret" to derive a key and nonce that are used for AEAD encryption of the sender data. "pseudocode (key, nonce) = PRF(sender_data_secret, sample) encrypted_sender_data = AEAD.Seal(key, nonce, sender_data_aad, sender_data) " The only differences between this construction and HN1 as described in NAN are (1) that it uses authenticated encryption instead of unauthenticated encryption and (2) that it protects information used to derive a nonce instead of the nonce itself. Since the "sender_data_secret" is distinct from the content encryption key, it follows that the sender data encryption scheme achieves AE2 security as defined in NAN, and therefore guarantees the confidentiality of the sender data. Use of the same "sender_data_secret" and ciphertext sample more than once risks compromising sender data protection by reusing an AEAD (key, nonce) pair.  For example, in many AEAD schemes, reusing a key and nonce reveals the exclusive OR of the two plaintexts.  Assuming the ciphertext output of the AEAD algorithm is indistinguishable from random data (i.e., the AEAD is AE1-secure in the phrasing of NAN), the odds of two ciphertext samples being identical is roughly 2 , i.e., the birthday bound. The AEAD algorithms for ciphersuites defined in this document all provide this property.  The size of the sample depends on the ciphersuite's hash function, but in all cases, the probability of collision is no more than 2 .  Any future ciphersuite MUST use an AE1-secure AEAD algorithm. 15.3. </ins> MLS does not provide confidentiality protection to some messages and fields within messages: as a guide to understanding how they might be exposed or protected in a given application.  <ins> 15.3.1. </ins> MLS provides no mechanism to protect the group ID and epoch of a message from the DS, so the group ID and the frequency of messages and epoch changes are not protected against inspection by the DS.  <ins> 15.3.2. </ins> A group's extensions are first set by the group's creator and then updated by GroupContextExtensions proposals.  A GroupContextExtension confidential to the group and the new joiner, then it will leak the group's extensions.  <ins> 15.3.3. </ins> The group's membership is represented directly by its ratchet tree, since each member's LeafNode contains members' cryptographic keys, a node to be less identifying, e.g., by using a pseudonymous credential and frequently rotating encryption and signature keys.  <ins> 15.4. </ins> The first form of authentication we provide is that group members can verify a message originated from one of the members of the group. that ensures that the legitimate members of the group have the required PSKs.  <ins> 15.5. </ins> Forward secrecy and post-compromise security are important security notions for long-lived MLS groups.  Forward secrecy means that This way, the key material used to add a member to a new group is more likely to be fresh and less likely to be compromised.  <ins> 15.6. </ins> KeyPackages are intended to be used only once.  That is, once a KeyPackage has been used to introduce the corresponding client to a KeyPackage publication system SHOULD rate-limit KeyPackage requests, especially if not authenticated.  <ins> 15.7. </ins> It is possible for a malicious member of a group to "fragment" the group by crafting an invalid UpdatePath.  Recall that an UpdatePath "SHA384", and "SHA512" refer to the SHA-256, SHA-384, and SHA-512 functions defined in SHS. <ins> In addition to the general requirements of ciphersuites, future ciphersuites MUST meet the requirements of confidentiality-of-sender- data. </ins> It is advisable to keep the number of ciphersuites low to increase the chances clients can interoperate in a federated environment, therefore the ciphersuites only include modern, yet well-established


For each non-empty leaf node, validate the LeafNode as described in leaf-node-validation. <del> For each non-empty parent node, verify that each entry in the node's "unmerged_leaves" represents a non-blank leaf node that is a descendant of the parent node. </del>  Identify a leaf whose LeafNode is identical to the one in the KeyPackage.  If no such field exists, return an error.  Let

In discussion of , it came up that the current tree validation rules are loose enough that they allow configurations of that could not come about by normal operations. This PR adds a check to enforce that if a node is unmerged at a parent node, it is also unmerged at every node between that parent node and the unmerged leaf. This is clearly a necessary property of any honestly-created tree, given the way entries are set and cleared (including the possible changes in ). I think it is also sufficient, but I don't have a proof on hand.
Note that this property is partly checked by parent hash validation, , but it still allows . So the main impact of this PR is to have an explicit, complete check in addition to the implicit, partial check in parent hash validation.
Independently of , I think this is a useful property to have: this is a key invariant that is used in the O(n log log n) algorithm for parent-hash verification described in and implemented in mlspp. I don't know how hard it would be to add enough checks to reject every "unnatural trees", for example I don't think it's possible to have a blank node whose children are non-blank internal nodes, but a tree having this thing would be accepted when joining a group?
When removing a participant, we blank its path up to the root because it might know the private key inside these nodes. We actually know precisely which nodes contain private key that are known to this participant: it is nodes for which the participant is not an umerged leaf. From this remark, we could design an alternative Remove that produces less blank nodes: blank all nodes on the path for which the participant is unmerged remove the participant from the unmerged leaves list of other nodes I think this would preserve the parent-hash invariants, but would need to check more thoroughly. (the intuition might rely on an invariant that is not checked on unmerged leaves, which is that the set of nodes for which a node is unmerged forms a connected path from the leaf going up [note that this invariant would have been true for free with !]) Pros: less blank nodes are produced removing a participant just after adding it does not change the tree, it's not that useful but it's a pretty property! Cons: the remove definition is more complex the optimization isn't great if the participant is merged for most nodes (e.g., it has issued an UpdatePath in the past) What do you think?
It sounds nice and it really sounds like it's secure enough, but I'm not sure it's not a bit too late for this.
My personal analysis here: If we do Remove like this, we should do Update as well. It's the same, you just don't remove from On the one hand, this gives some better symmetry to the calculations and might make things slightly faster On the other hand, this is pretty late for a substantive change and I don't expect the performance gain to be huge. Overall, I'm more worried about the security analysis than the practicalities. If NAME can get his model updated and checked in a timely manner, I would probably take this change. (URL detailed notes below this line...) Practically speaking, it's a small spec diff. (Note that we should cover Update here as well, which requires slightly different logic.) I expect the implementation diff will be similarly small, something like:
I think this would introduce a new attack. The unmerged leaves entry effectively becomes a "do not delete" marker for parent nodes. An attacker can use this to stay in the tree longer than intended and thus violate the tree invariant. Similar to the attack Joel presented a while back, the attacker can prepare a special tree in the following way: The adds its leaf index to the unmerged leaves list of all of its direct path parent nodes. The attacker also updates its parent hash extension (by simulating a fake Commit) so that the new parent hash now covers the updated unmerged leaves lists. The attacker presents this new ratchet tree to the victim.The victim joins the group either by Welcome or by External Commit When validating the ratchet tree, the victim does not detect the ruse because all parent hash extensions from all leaves in the tree check out. The victim (or anyone who joined after the victim) proceeds to remove the attacker from the group. While doing so, the victim now only blanks the attacker's leaf node and leaves the attacker's direct path intact because of the markers. The attacker is now seemingly removed, but still knows secrets of the ratchet tree. This can possibly be prevented by introducing additional validation for new joiners, but it seems very brittle nonetheless. Given that in a group where updates are frequent, this wouldn't bring much of a performance benefit I would not be inclined to go forward with this. We would need much more analysis and it's a bit late for that.
Can you explain the attack more thoroughly with actual tree examples? I'm having trouble understanding it.
NAME I don't think the attack works as you describe it, or if it does, we need stronger parent hash validation rules regardless of this PR. Any node that has ever sent a Commit should not be unmerged, so it should be impossible to both have a leaf sign a parent hash chain and appear as unmerged in any of its parents, as happens in your putative tree. I agree that some analysis is needed. The only question is how quickly it can be done. What I'm wondering is whether some of NAME earlier work can be updated.
NAME Yes, stronger validation rules are needed. Right now what you describe is not enforced, hence my qualification as an attack. I also agree that these rules could be added regardless.
I don't understand why we need stronger validation rules. When joining a group, invariants are checked on the tree, like the well-formedness of unmerged leaves or the validity of parent-hashes. A tree with these invariants is then secure: the signature of the leaf parent-hash-linked to a node covers the whole subtree at the time of signing, which forbids the old double-join attacks. You can't break TreeKEM's invariants without breaking the parent-hash invariants. Therefore I think we just need to prove that the new Remove preserves the MLS invariants (unmerged leaves are well-formed + every node is parent-hash linked to a node below it). If that is true, I can update my formal proofs quickly I think.
NAME As I understand what NAME is proposing, the idea is that the attacker A prepares a tree of the following form, where: [A] indicates A is unmerged But A knows Q and R A's leaf signs a parent hash chain (A -> Q -> R) B need not be aware any of this is going on In other words, it's like A added C to (A, B) in a full Commit, but with A also marked as unmerged at Q and R. A sends this tree to C in a Welcome. If C subsequently tries to remove A, then according to this PR, you would end up with the following tree: Since the attacker A knows Q, and C encrypted the path secret for R' to Q, A still knows the group secrets.
Right now we have the following validation rules in the spec: Verify the integrity of the ratchet tree. Verify that the tree hash of the ratchet tree matches the treehash field in GroupInfo. For each non-empty parent node, verify that it is "parent-hash valid", as described in . For each non-empty leaf node, validate the LeafNode as described in . For each non-empty parent node, verify that each entry in the node's unmergedleaves represents a non-blank leaf node that is a descendant of the parent node. Only the last rule concerns the unmerged leaves. That rule is not enough to prevent an attacker who is a regular member of the tree to add its leaf index to the unmerged leaves list of all the parent nodes in its direct path.
NAME Thanks for the illustration, that's exactly what I had in mind.
For D to be parent-hash-linked to P (with child C), there is the following rule: With D = C = A and P = Q, the intersection of P's unmerged leaves with the subtree under C is [A], and the resolution of C with D removed is []. Therefore the tree will be rejected by the participant processing the Welcome.
Note that it is similar to the example 2 of , and that parent-hash was modified to prevent these kind of modifications.
NAME It looks like the parent hash verification rule you mention does indeed prevent the attack. Given how unintuitive that rule is I a) missed it and b) I cannot say if it prevents the attack in all scenarios (although my intuition is it does). So it really comes down to proofs at this point.
The rule is a ninja equation designed by NAME in . I agree that it is not easy to understand! It captures two facts: D and P were updated at the same time, and their unmerged leaves is coherent with this fact: the nodes between D and P were filtered, hence they are blank, and the co-path subtree are mostly empty (they might contain unmerged leaves of P) From this we can prove the strong guarantees discussed in and .
Note: if you look at the revised parent-hash link conditions in , it's much easier to understand why the attack doesn't work: it's the condition which will be broken.
I thought I had an attack case, based on the following tree ([B]` entries), but the original sibling tree hash stopped it. Which points toward a simple, intuitive argument for why this proposal is OK: Claim: Given an honestly constructed tree, a malicious group member can't modify it to add themselves to unmergedleaves for a node whose private key they know Proof: If they know the private key because they set it, you can't be unmerged in it because If they know the private key because the received it from someone else, then their leaf is covered by the original sibling subtree hash. If they add themselves to unmerged_leaves, they'll be removed from the original sibling tree, and that hash will be invalid.
NAME Is the F* spec in mls-proposal up-to-date enough so that I can double check how things currently work there? Or did you ninja this by hand :D ?
NAME the parent-hash link invariant is defined and the parent hash guarantees theorem is proved , and Richard found the ninja equation if that's what you're asking for! NAME I don't understand why you closed the issue as completed? The proof goes as follows. From the parent hash guarantees, we know that the signature covers all the subtree it the participant modified, at the time of their last modification (we name them the "original subtrees"). When a private key is generated for a node, the participant generating it: send it to the leaves in the corresponding subtree signs the corresponding subtree (via parent-hash) Note that in every subtree signature (via parent-hash), there is the property that the private hpke key of the node is known only by the leaves of that subtree. When you receive tree, you check every signature and compute every original subtree, and get back the property that the private hpke key of each node is known only to leaves in its original subtree. Therefore, if you have an attack, it means you found a way to break the invariants checked when joining a group. It might be the case that the new Remove does break the invariants and we would need to strengthen them (as tried in ), but my feeling is that it should work fine without additional invariants. I'll check in the next days and keep you up-to-date!
Thinking about an actual proof, I find that it does break the invariants: Suppose we start with the following tree (which is valid, although we can't get it with normal operations) Removing C makes: It breaks the parent-hash link X -> Y, because the original sibling tree hash is not the same. In practice, it's not possible to have the situation with C unmerged for Y but not for Z, but it's not an invariant checked when joining a group, so we can't rely on it… Note that this invariant is guaranteed for free with the unmerged leaves defined as in :)
I'm sorry, I hit the wrong button when commenting!
It's concerning that we have trees that are valid according to the validation rules but can't be produced by normal operations. It seems like that calls for more validation. I noticed as I was looking at this yesterday that I don't think we are validating two (equivalent) properties that are true for every real case of an unmerged leaf: If X is unmerged at P, then it is unmerged at every node below P (P's descendants) If X is not unmerged at P, then it is not unmerged at every node above P (P's ancestors) More generally, I wonder if there's a connection here, that unmerged Remove is safe if and only if the tree validation rules prevent trees that could not have arisen naturally. I would argue that we should fix the latter issue anyway. I filed for this.
Hi, I don't think this optimization is worth it. At least in the IM use case, I see lots of problems anytime there is a build-up of unmerged leaves. I see unmerged leaves as a transient situation that should not be encouraged nor optimized.
The optimization does seem elegant, but at the very least this change should be brought to the list. In particular, I'd love to hear NAME and NAME opinion on this from a provable security perspective before we consider merging this into the spec.
After discussion at the interim 2022-12-16, there was agreement that this change is not worth making.

For each non-empty leaf node, validate the LeafNode as described in leaf-node-validation.  <ins> For each non-empty parent node and each entry in the node's "unmerged_leaves" field: Verify that the entry represents a non-blank leaf node that is a descendant of the parent node. Verify that every non-blank intermediate node beween the leaf node and the parent node also has an entry for the leaf node in its "unmerged_leaves". </ins> Identify a leaf whose LeafNode is identical to the one in the KeyPackage.  If no such field exists, return an error.  Let


they need to update their GroupState with information about the new member:  A group member generates this message by requesting a UserInitKey from the directory for the user to be added, and encoding it into an Add message. An existing member receiving a Add message first verifies the signature on the message, then updates its state as follows: <del> Increment the size of the group </del>  Verify the signature on the included UserInitKey; if the signature verification fails, abort <del> Append an entry to the roster containing the credential in the </del>  included UserInitKey <del> Update the ratchet tree by adding a new leaf node for the new member, containing the public key from the UserInitKey in the Add corresponding to the ciphersuite in use </del> Update the ratchet tree by setting to blank all nodes in the <del> direct path of the new node, except for the leaf (which remains set to the new member's public key) </del>  The update secret resulting from this change is an all-zero octet string of length Hash.length. Update the ratchet tree by replacing nodes in the direct path from the removed leaf using the information in the Remove message  Update the ratchet tree by setting to blank all nodes in the direct path of the removed leaf

Right now, we blank out leaf nodes when we remove their holders, but we never reduce the size of hte tree or repopulate interior blanks with new members. This PR adds those functions: Reduce the size of the tree and the roster when you remove from the right edge Add an field to the message, to enable new members to be added to blank slots in the tree, not just at the right edge. In addition to performing "garbage collection", this change should make it easier to do a state resync -- just remove the desync'ed endpoint and add them back in the same spot.
NAME Do you think this is ready to merge? say it's OK as long as we can convince ourselves that truncation preserves the tree invariant. Proof sketch: The tree invariant is a universal statement over nodes in the tree and members in the group: ! Since subtrees are just subsets of the set of nodes in the tree, if the tree invariant holds for a given tree, then it also holds for any subtree of that tree. So since truncation just moves from a tree to a subtree, the invariant continues to hold.
Currently, the tree only ever grows: When you add someone, you have to add them at the right edge, even if there are blank leaves When you remove someone, the tree never shrinks We should enable reclaiming of blank leaves and automatically reduce the size of the tree to the minimum necessary size.
After discussion at 2019-01 interim: Agreement that this seems OK as long as we can verify that the tree invariant holds when you truncate the tree.

they need to update their GroupState with information about the new member: <ins> The "index" field indicates where in the tree the new member should be added.  The new member can be added at an existing, blank leaf node, or at the right edge of the tree.  In any case, the "index" value MUST satisfy "0 <= index <= n", where "n" is the size of the group.  The case "index = n" indicates an add at the right edge of the tree).  If "index < n" and the leaf node at position "index" is not blank, then the recipient MUST reject the Add as malformed. </ins> A group member generates this message by requesting a UserInitKey from the directory for the user to be added, and encoding it into an Add message. An existing member receiving a Add message first verifies the signature on the message, then updates its state as follows:  <ins> If the "index" value is equal to the size of the group, increment the size of the group, and extend the tree and roster accordingly </ins> Verify the signature on the included UserInitKey; if the signature verification fails, abort  <ins> Set the roster entry at position "index" to the credential in the </ins> included UserInitKey  Update the ratchet tree by setting to blank all nodes in the  <ins> direct path of the new node Set the leaf node in the tree at position "index" to a new node containing the public key from the UserInitKey in the Add corresponding to the ciphersuite in use </ins> The update secret resulting from this change is an all-zero octet string of length Hash.length. Update the ratchet tree by replacing nodes in the direct path from the removed leaf using the information in the Remove message <ins> Reduce the size of the roster and the tree until the rightmost element roster element and leaf node are non-null </ins> Update the ratchet tree by setting to blank all nodes in the direct path of the removed leaf


And its fields set to: Here, the functions "Signature.Sign" and "Signature.Verify" are <del> defined by the signature algorithm. </del>  The ciphersuites are defined in section mls-ciphersuites. Applications SHOULD provide a unique label to "MLS-Exporter" that identifies the secret's intended purpose.  This is to help prevent the same secret from being generated and used in two different <del> places. </del>  The exported values are bound to the group epoch from which the "exporter_secret" is derived, and hence reflect a particular state of MLS Credential Types (mls-credential-types)  All of these registries should be under a heading of "Messaging Layer Security", and assignments are made via the Specification Required policy RFC8126.  See de for additional information about the MLS Name: The name of the wire format <del> Recommended: Whether support for this wire format is recommended by the IETF MLS WG.  Valid values are "Y" and "N".  The "Recommended" column is assigned a value of "N" unless explicitly requested, and adding a value with a "Recommended" value of "Y" requires Standards Action RFC8126.  IESG Approval is REQUIRED for a Y->N transition. </del>  Reference: The document where this wire format is defined GI: GroupInfo objects <del> Recommended: Whether support for this extension is recommended by the IETF MLS WG.  Valid values are "Y" and "N".  The "Recommended" column is assigned a value of "N" unless explicitly requested, and adding a value with a "Recommended" value of "Y" requires Standards Action RFC8126.  IESG Approval is REQUIRED for a Y->N transition. </del>  Reference: The document where this extension is defined Name: The name of the proposal type <del> Recommended: Whether support for this extension is recommended by the IETF MLS WG.  Valid values are "Y" and "N".  The "Recommended" column is assigned a value of "N" unless explicitly requested, and adding a value with a "Recommended" value of "Y" requires Standards Action RFC8126.  IESG Approval is REQUIRED for a Y->N transition. </del>  Path Required: Whether a Commit covering a proposal of this type is required to have its "path" field populated (see commit). Name: The name of the credential type <del> Recommended: Whether support for this credential is recommended by the IETF MLS WG.  Valid values are "Y" and "N".  The "Recommended" column is assigned a value of "N" unless explicitly requested, and adding a value with a "Recommended" value of "Y" requires Standards Action RFC8126.  IESG Approval is REQUIRED for a Y->N transition. </del>  Reference: The document where this credential is defined 16.6.  Specification Required RFC8126 registry requests are registered after a three-week review period on the MLS DEs' mailing list: mls-reg- review@ietf.org [1], on the advice of one or more of the MLS DEs. be perceived as creating a conflict of interest for a particular MLS DE, that MLS DE SHOULD defer to the judgment of the other MLS DEs. <del> 16.7. </del>  This document registers the "message/mls" MIME media type in order to allow other protocols (e.g., HTTP RFC7540) to convey MLS messages.

Also copies the "Recommended" field by reference, instead of reciting it in each registry. Note that this has a conflict with that will not catch -- "MLSContentTBS" will have to be changed to whatever new name is agreed in that PR.
The of absolutely needs to be unique for every signature happening in the protocol, for security and avoid signature ambiguity. That includes signatures happening in extensions. Should this be referenced in a IANA considerations to ensure there is no conflict in the future? Or at least say something about this somewhere? We have to think how to design things well here to be future-proof: the structure must be used for every signature done with the leaf signature keys, which includes what might happen in extensions but also in future versions of MLS. If future versions of MLS could avoid doing , that would be great!
This seems to come down to whether we expect the signature keys used for MLS authentication to be used outside of MLS (otherwise there's no risk of confusion) and if so, whether this spec has any influence on that usage. If you're using your MLS key to sign Bitcoin transactions, we can't do much to help that. Note that there is not an issue for future version of MLS here. The label is constructed as , so a future version could just increment the part of that. Given all that, I might prefer a recommendation that signature keys not be reused across contexts, rather than trying to accommodate reuse. If we do something here, we should do something analogous to the registry, which are similarly intended for domain separation between different use cases. We should probably consider making an exporter label registry anyway.
I agree, we shouldn't do anything about someone using the MLS signature key in another protocol. About future MLS versions: I agree, but note that there is a length tag before that. It means that we impose to all signature of future MLS versions to start with (including the quic integer) Is it something acceptable or not, I don't now. I just think we should have in mind that the decisions we make now on will have an impact on the design of future MLS versions. About extensions: in the extensions document, there is a signature computed with If two extensions were to use the same label then this could lead to security problems, this is why I think the labels should be in some registry to ensure they are all unique. One other thing we could do is to separate all extensions signatures from the core MLS signatures, for exemple using the label "MLS 1.0 extension [label from extension]"? Like that a bogus extension can't introduce a security flaw in core MLS that way?

And its fields set to: Here, the functions "Signature.Sign" and "Signature.Verify" are  <ins> defined by the signature algorithm.  If MLS extensions require signatures by group members, they should re-use the SignWithLabel construction, using a distinct label.  To avoid collisions in these labels, an IANA registry is defined in mls-signature-labels. </ins> The ciphersuites are defined in section mls-ciphersuites. Applications SHOULD provide a unique label to "MLS-Exporter" that identifies the secret's intended purpose.  This is to help prevent the same secret from being generated and used in two different  <ins> places.  To help avoid the same label being used in different applications, an IANA registry for these labels has been defined in mls-exporter-labels. </ins> The exported values are bound to the group epoch from which the "exporter_secret" is derived, and hence reflect a particular state of MLS Credential Types (mls-credential-types) <ins> MLS Signature Labels (mls-signature-labels) MLS Exporter Labels (mls-exporter-labels) </ins> All of these registries should be under a heading of "Messaging Layer Security", and assignments are made via the Specification Required policy RFC8126.  See de for additional information about the MLS Name: The name of the wire format  <ins> Recommended: Same as in </ins> Reference: The document where this wire format is defined GI: GroupInfo objects  <ins> Recommended: Same as in </ins> Reference: The document where this extension is defined Name: The name of the proposal type  <ins> Recommended: Same as in </ins> Path Required: Whether a Commit covering a proposal of this type is required to have its "path" field populated (see commit). Name: The name of the credential type  <ins> Recommended: Same as in </ins> Reference: The document where this credential is defined 16.6. <ins> The "SignWithLabel" function defined in ciphersuites avoids the risk of confusion between signatures in different contexts.  Each context is assigned a distinct label that is incorporated into the signature. This registry records the labels defined in this document, and allows additional labels to be registered in case extensions add other types of signature using the same signature keys used elsewhere in MLS. Template: Label: The string to be used as the "Label" parameter to "SignWithLabel" Recommended: Same as in Reference: The document where this credential is defined Initial contents: 16.7. The exporter function defined in exporters allows applications to derive key material from the MLS key schedule.  Like the TLS exporter RFC8446, the MLS exporter uses a label to distinguish between different applications' use of the exporter.  This registry allows applications to register their usage to avoid collisions. Template: Label: The string to be used as the "Label" parameter to "MLS- Exporter" Recommended: Same as in Reference: The document where this credential is defined The registry has no initial contents, since it is intended to be used by applications, not the core protocol.  The table below is intended only to show the column layout of the registry. 16.8. </ins> Specification Required RFC8126 registry requests are registered after a three-week review period on the MLS DEs' mailing list: mls-reg- review@ietf.org [1], on the advice of one or more of the MLS DEs. be perceived as creating a conflict of interest for a particular MLS DE, that MLS DE SHOULD defer to the judgment of the other MLS DEs.  <ins> 16.9. </ins> This document registers the "message/mls" MIME media type in order to allow other protocols (e.g., HTTP RFC7540) to convey MLS messages.


6.3.1. <del> Content to be encrypted is encoded in an PrivateContentTBE structure. </del>  The "padding" field is set by the sender, by first encoding the content (via the "select") and the "auth" field, then appending the contains an object of the following form, with the values used to identify the key and nonce: <del> When decoding an PrivateContentTBE, the application MUST check that </del>  the FramedContentAuthData is valid. It is up to the application to decide what "authenticated_data" to are represented as absolute times, measured in seconds since the Unix epoch (1970-01-01T00:00:00Z).  Applications MUST define a maximum total lifetime that is acceptable for a LeafNode, and reject any <del> LeafNode where the total lifetime is longer than this duration. </del>  In the case where the leaf node was inserted into the tree via a Commit message, the "parent_hash" field contains the parent hash for the client, e.g., a proposal, a commit, or a ratchet tree of the group the client is joining, it is RECOMMENDED that the client verifies that the current time is within the range of <del> the "lifetime" field. </del>  Verify that the extensions in the LeafNode are supported by checking that the ID for each extension in the "extensions" field If encoding as PublicMessage, compute the "membership_tag" value using the "membership_key". <del> If encoding as an PrivateMessage, encrypt the message using the </del>  "sender_data_secret" and the next (key, nonce) pair from the sender's handshake ratchet.

URL
:+1: Thank you very much for all of the explicitly worked examples. What is the reason for MUST vs RECOMMENDED difference in the two different circumstances when validating the `lifetime` field? Consider adding a platitude about communicating nodes SHOULD use some method of time synchronization... "top of these basic mechanism" -> "top of these basic mechanisms" (or maybe "top of this basic mechanism") "in an PrivateContentTBE structure" -> "in a PrivateContentTBE structure"

6.3.1.  <ins> Content to be encrypted is encoded in a PrivateContentTBE structure. </ins> The "padding" field is set by the sender, by first encoding the content (via the "select") and the "auth" field, then appending the contains an object of the following form, with the values used to identify the key and nonce:  <ins> When decoding a PrivateContentTBE, the application MUST check that </ins> the FramedContentAuthData is valid. It is up to the application to decide what "authenticated_data" to are represented as absolute times, measured in seconds since the Unix epoch (1970-01-01T00:00:00Z).  Applications MUST define a maximum total lifetime that is acceptable for a LeafNode, and reject any  <ins> LeafNode where the total lifetime is longer than this duration.  In order to avoid disagreements about whether a LeafNode has a valid lifetime, the clients in a group SHOULD maintain time synchronization (e.g., using the Network Time Protocol RFC5905). </ins> In the case where the leaf node was inserted into the tree via a Commit message, the "parent_hash" field contains the parent hash for the client, e.g., a proposal, a commit, or a ratchet tree of the group the client is joining, it is RECOMMENDED that the client verifies that the current time is within the range of  <ins> the "lifetime" field.  (This check is not mandatory because the LeafNode might have expired in the time between when the message was sent and when it was received.) </ins> Verify that the extensions in the LeafNode are supported by checking that the ID for each extension in the "extensions" field If encoding as PublicMessage, compute the "membership_tag" value using the "membership_key".  <ins> If encoding as a PrivateMessage, encrypt the message using the </ins> "sender_data_secret" and the next (key, nonce) pair from the sender's handshake ratchet.


RFC EDITOR PLEASE DELETE THIS SECTION.  draft-02 Removed ART (*)

I forgot to update the changelog before I published -03. We should add that to the review criteria. This will at least fix it for -04.

RFC EDITOR PLEASE DELETE THIS SECTION. <ins> draft-03 Added ciphersuites and signature schemes (*) Re-ordered fields in UserInitKey to make parsing easier (*) Fixed inconsistencies between Welcome and GroupState (*) Added encryption of the Welcome message (*) </ins> draft-02 Removed ART (*)


Update messages SHOULD be sent at regular intervals of time as long as the group is active, and members that don't update SHOULD eventually be removed from the group.  It's left to the application <del> to determine an appropriate amount of time between Updates.  In general, however, applications should take care that they do not send MLS messages at a rate that overwhelms the transport over which messages are being sent. </del>  Members are removed from the group in a similar way, as shown in remove-flow.  Any member of the group can send a Remove proposal

URL Thanks for working on this interesting topic. Thanks for Gorry fairhost for this TSVART review. I have few comments, which I believe if addressed will improve the document. - There is a mismatch between intended status of this spec (PS) and what in the doc header (Informational). I see Lars already have a discuss on it, so supporting it. - The specification says the UPDATE messages SHOULD be periodically sent and expects the applications to decided on the period between UPDATE messages. As I can see there are multiple implementations, can't we also recommend some guidelines and considerations on how to select the period? In some discussions I saw we were talking about hours and months.. - it says - "In general, however, applications should take care that they do not send MLS messages at a rate that overwhelms the transport over which messages are being sent." I am not sure I understand what "overwhelms the transport" means. Are we talking about the congestion control or bandwidth estimation part of transport protocols? Usually the application have no or very vague information about the internal protocol states, in that case how the applications are expected to take care of this? are we suggesting some interaction application rate control?

Update messages SHOULD be sent at regular intervals of time as long as the group is active, and members that don't update SHOULD eventually be removed from the group.  It's left to the application  <ins> to determine an appropriate amount of time between Updates.  Since the purpose of sending an Update is to proactively constrain a compromise window, the right frequency is usually on the order of hours or days, not milliseconds. The MLS architecture recommends that MLS be operated over a secure transport (see Section 7.1 of I-D.ietf-mls-architecture).  Such transport protocols will typically provide functions such as congestion control that manage the impact of an MLS-using application on other applications sharing the same network.  Applications should take care that they do not send MLS messages at a rate that will cause problems such as network congestion, especially if they are not following the above recommendation (e.g., sending MLS directly over UDP instead). </ins> Members are removed from the group in a similar way, as shown in remove-flow.  Any member of the group can send a Remove proposal


RFC EDITOR PLEASE DELETE THIS SECTION.  draft-15 Include ciphersuite in group context (*)

Also adds a draft-18 section to cover any edits between now and the next I-D.

RFC EDITOR PLEASE DELETE THIS SECTION. <ins> draft-18 Make the document standards track Make the ratchet tree non-malleable (*) Use ExpandWithLabel to derive welcome key (*) Change MLS-Exporter label from "exporter" to "exported" (*) Loosen chain requirements (*) Clarify transcript hash initialization GREASE for MLS registries Move pseudocode out of KDFLabel definition. Rename PrivateContentTBE to PrivateMessageContent Fix DecryptWithLabel argument order for Welcome Responses to IESG reviews Describe varint length check more clearly draft-17 Rename MLSCiphertext and MLSPlaintext to PrivateMessage and PublicMesssage respectively (*) Add label and context to public-key encryption (*) Include leaf index in LeafNodeTBS for better parent-hash guarantees (*) Make ProtocolVersion two bytes (*) Clarify group creation (*) Validate additional properties of unmerged leaves (*) Clarify that the AS needs to vet the signature key Remove "MLS" prefix on structs Credentials should be replaced before expiring Add a section discussing the security of the sender data protection Minor fixes in presentation language. Allow multiple welcomes per commit Remove reference to BasicCredential. Client aware of its own removal in group Create IANA registries for signature and export labels Complete IANA media type registration Make more vendor code points available Update Recommended column definition to match 8447bis Responses to early ARTART review Responses to early OPSDIR review Responses to early TSV-ART review draft-16 Fix GroupInfoTBS (*) Make reference to h2 informative </ins> draft-15 Include ciphersuite in group context (*)


former is used to update the confirmed transcript hash and the latter to update the interim transcript hash. <del> In this notation, "InterimTranscriptHashInput_[epoch]" and "ConfirmedTranscriptHashInput_[epoch+1]" are based on the Commit that initiated the epoch with epoch number "epoch".  (Note that the "epoch" field in this Commit will be set to "epoch - 1", since it is </del>  sent within the previous epoch.)  8.3. In addition to initializing a new epoch via KDF invocations as

The current text on initializing the transcript hash is ambiguous. In particular, it never tells you how to compute the first . This PR provides clearer initialization instructions, and a figure that summarizes the transcript hash evolution [2]. [1] To be clear, I think the intent is clear and all current implementations are consistent. The text is just bad. [2] The diagram is a little over-the-top elaborate. I would be OK removing it :)
I generally like the diagram, but the intersection between , and is not clear to me. What is going where? Also shouldn't and also get numbers? And what's the different between the and suffix?
OK, I rotated the diagram 90 degrees and rejiggered things to hopefully be clearer. It now should just barely fit in the .TXT format. What do folks think?
Beautiful!

former is used to update the confirmed transcript hash and the latter to update the interim transcript hash.  <ins> In this notation, "ConfirmedTranscriptHashInput_[epoch]" and "InterimTranscriptHashInput_[epoch]" are based on the Commit that initiated the epoch with epoch number "epoch.  (Note that the "epoch"field in this Commit will be set to "epoch - 1`, since it is </ins> sent within the previous epoch.) <ins> The transcript hash "ConfirmedTranscriptHashInput_[epoch]" is used as the "confirmed_transcript_hash" input to the "confirmation_tag" field for this Commit.  Each Commit thus confirms the whole transcript of Commits up to that point, except for the latest Commit's confirmation tag. </ins> 8.3. In addition to initializing a new epoch via KDF invocations as


If the tree has "2^d" leaves, then it has "2^(d+1) - 1" nodes.  The "ratchet_tree" vector logically has this number of entries, but the <del> sender SHOULD NOT include blank nodes after the last non-blank node. If a receiver encounters a vector whose length "L" is not of the form "2^(d+1) - 1", then the receiver MUST extend it to the right with blank values until it has such a length, adding the minimum number of blank values possible.  (Obviously, this may be done "virtually", by synthesizing blank nodes when required, as opposed to actually changing the structure in memory.) </del>  The leaves of the tree are stored in even-numbered entries in the array (the leaf with index "L" in array position "2*L").  The root

As discussed in mlswg/mls-implementations . Currently, there are several valid for one tree: the sender can choose ("SHOULD") to omit some (but not all) blank nodes at the right of the tree. This PR changes this: the sender MUST omit all the blank nodes at the right of the tree, and the receiver MUST check that it was done correctly (i.e., the last node of is non-blank).
Right now, the signature on an MLSPlaintext includes the GroupContext for the group. If we are going to allow Add proposals signed by the new member, then this will need to change, because the new member doesn't know the GroupContext. My preference would be to simply remove the GroupContext from the plaintext. The Group ID is already included, so copy/paste protection can be added that way. The other elements of the GroupContext effectively overlap with the Epoch that is already signed.
Haven't thought about this too deeply, but having the full GroupContext in MLSPlaintextTBS means that we know all the members agree on the state of the ratchet tree, the transcript, the extensions. Removing it means we would no longer have agreement on those, right?
We won't have confirmation of agreement as a result of signature verification. The question is whether that is necessary. If the MLSPlaintext was decrypted from an MLSCiphertext, for example, then the successful decryption of the MLSCiphertext implies agreement on all those things. I suppose you could also switch the signature input based on SenderType, but that seems messier.
I'm not so sure that decrypting an MLSCiphertext implies agreement, because the crypto is symmetric. A man-in-the-middle can re-encrypt but not re-sign.

If the tree has "2^d" leaves, then it has "2^(d+1) - 1" nodes.  The "ratchet_tree" vector logically has this number of entries, but the  <ins> sender MUST NOT include blank nodes after the last non-blank node. The receiver MUST check that the last node in "ratchet_tree" is non- blank, and extend it to the right until it has a length of the form "2^(d+1) - 1", adding the minimum number of blank values possible. (Obviously, this may be done "virtually", by synthesizing blank nodes when required, as opposed to actually changing the structure in memory.) </ins> The leaves of the tree are stored in even-numbered entries in the array (the leaf with index "L" in array position "2*L").  The root


framing.  Security notions such as forward secrecy and post- compromise security are defined in security-considerations.  2.1. We use the TLS presentation language RFC8446 to describe the a LeafNode MUST be included in the "extensions" field of the "capabilities" field, and the credential type used in the LeafNode MUST be included in the "credentials" field of the "capabilities" <del> field. </del>  The "leaf_node_source" field indicates how this LeafNode came to be added to the tree.  This signal tells other members of the group The field "leaf_node.leaf_node_source" of the LeafNode in a KeyPackage MUST be set to "key_package". <del> Extension included in the "extensions" or "leaf_node.extensions" fields MUST be included in the "leaf_node.capabilities" field. </del>  10.1. creator of a GroupInfo may recompute the confirmation tag as "MAC(confirmation_key, confirmed_transcript_hash)".)  New members MUST verify that "group_id" is unique among the groups they're currently participating in. advertised in it.  Otherwise, another client might fail to interoperate by selecting one of those parameters. <del> A client initiating a group MUST ignore all unrecognized ciphersuites, extensions, and other parameters.  Otherwise, it may fail to interoperate with newer clients. </del>  Any field containing a list of extensions MUST NOT have more than one extension of any given type. extensions.  The "capabilities" field MUST indicate support for each extension in the GroupContext. <del> If any extension in a GroupInfo message is unrecognized (i.e., not contained in the "capabilities" of the corresponding KeyPackage), then the client MUST reject the Welcome message and not join the </del>  group. <del> Note that the latter two requirements mean that all MLS extensions are mandatory, in the sense that an extension in use by the group MUST be supported by all members of the group. </del>  The parameters of a group may be changed by sending a <del> GroupContextExtensions proposal to enable additional extensions, or by reinitializing the group as described in reinitialization. </del>  14.

This PR is an attempt to address . It registers GREASE code points in the relevant registries and recommends their use in the appropriate places. It also includes some edits to the extensibility provisions to make them clearer and sharper, and a little cleanup on registry formatting.
(First off, I know that I'm super late in the process so feel free to tell me to go away and I'll do that.) I see that MLS is creating . I think it would benefit the protocol to reserve a few codepoints from all of these registries for . That'll help avoid ossification down the line.
NAME How would this work in MLS. In GREASE the client proposes values to a server (which the server should never support). The client is expected to propose these values and also reject them. This works fine because clients don't talk to clients. In MLS, clients send messages directly to other clients, and create groups based on capabilities advertised in KeyPackages. How would you prevent a "dummy" ciphersuite or version number from being used in an MLS group?
The GREASE RFC is specific to TLS where extensions are client-initiated, but the concept applies anywhere and is not specific to client-server protocols. To support GREASE, all we need is: reserve some codepoints in the IANA registry to avoid future collisions when sender sends its list of supported values, it randomly adds a value from the GREASE set no changes are made on the receiver Because of this, the receivers will ignore the GREASE codepoints and there is no risk of negotiating them. Another way to say this is: don't implement GREASE by adding the codepoints to your supported set, instead only add a random one from your send serialization code.
Some thoughts: I think we expect many/most MLS deployments to be single-party, where the same organization controls the client and server software. As such, GREASE has less value than in a TLS context, because if that organization wants to exercise a new extension point they can easily correct any errors in their software that would prevent them from doing so. MLS is also expected to be layered on top of an encrypted transport protocol, which prevents third-party meddling to begin with. On a more philosophical note -- If GREASE is supposed to prevent poorly maintained client software or middleware from breaking the protocol when extension points are exercised that they don't understand, then does it really still work if GREASE is a core part of the protocol? If the semantic of some field in the protocol is that unexpected values should be ignored, it seems just as easy for a negligent engineer to write the code "break if there's something besides X, Y, or Z" as it is to write "break if there's something besides X, Y, Z, or one of the GREASE codepoints". Correspondingly, I'm not sure that we have historical validation from TLS that GREASE works.
We have some solid validation from HTTP that grease works. In both HTTP/2 and HTTP/3, receivers are supposed to ignore unknown frames. HTTP/2 was launched without grease, and trying to use extensions caused enough breakage that they had to be removed. HTTP/3 launched with grease, and a few implementations send random data, so broken servers were found and fixed before they got prevalent. Regarding the philosophical point, grease isn't meant to avoid malicious implementors - you can always write code that looks at grease. Grease is meant to avoid lazy implementors, of which we have solid existence proofs.
Right, I wasn't thinking of malicious implementors, just negligent ones. There are places where the "receiver" portion of an MLS client does need to be aware of GREASE. These immediately come to mind: A extension shouldn't be allowed to contain GREASE codepoints even though all members may "support" that codepoint Proposal types that are GREASE shouldn't be allowed to be sent even though all members may "support" that proposal type GREASE codepoints shouldn't be considered when deciding whether a group uses the "best" version & ciphersuite for all members, as one of these codepoints may naively look "better" than a real one These would all seem to carry the risk of accidentally writing the code I said earlier, "break if there's something besides X, Y, Z, or one of the GREASE codepoints" because GREASE is being intentionally excluded before those other checks happen. Fwiw, I'm not against this idea, I'm just not sure of the right way to translate it to MLS.
That's not right, the whole point of grease is for it to not modify receiver code. No one ever "supports" grease code points, they just randomly send them. Since receiving an unknown value in would break a group, then we definitely should not send grease in that scenario. Determining the "best" version/ciphersuite/etc requires first making the intersection set of supported versions, and grease would never be in that set since no one "supports" grease code points.
If that is the case, then it seems you have eliminated all the places you might have used GREASE. I don't there is an obvious translation because MLS is fundamentally a clients-to-clients protocol instead of a client-to-server protocol.
Grease isn't about clients or servers, it's about senders and receivers - which MLS has. When a clients sends its capabilities, it can for example grease the list of cipher suites - that won't break a group because receivers ignore cipher suites they don't support.
Another way to think about this: you can (and should) grease anything where recipients ignore unknown/unrecognized values. So for MLS, I'd base the list off of this text:
Just to make this a little more concrete, I think that this is the text we would want GREASE around: So a PR would basically do the following: Register GREASE values for ciphersuites, extension types, proposal types, credential types Recommend that clients include a random selection of GREASE values in: The , , , and fields of The fields of and (which are effectively ephemeral), with random extension data

framing.  Security notions such as forward secrecy and post- compromise security are defined in security-considerations. <ins> As detailed in grease, MLS uses the "Generate Random Extensions And Sustain Extensibility" (GREASE) approach to maintaining extensibility, where senders insert random values into fields in which receivers are required to ignore unknown values.  Specific "GREASE values" for this purpose are registered in the appropriate IANA registries. </ins> 2.1. We use the TLS presentation language RFC8446 to describe the a LeafNode MUST be included in the "extensions" field of the "capabilities" field, and the credential type used in the LeafNode MUST be included in the "credentials" field of the "capabilities"  <ins> field.  As discussed in extensibility, unknown values in "capabilities" MUST be ignored, and the creator of a "capabilities" field SHOULD contain some random GREASE values to help ensure that other clients correctly ignore unknown values. </ins> The "leaf_node_source" field indicates how this LeafNode came to be added to the tree.  This signal tells other members of the group The field "leaf_node.leaf_node_source" of the LeafNode in a KeyPackage MUST be set to "key_package".  <ins> Extensions included in the "extensions" or "leaf_node.extensions" fields MUST be included in the "leaf_node.capabilities" field.  As discussed in extensibility, unknown extensions in "KeyPackage.extensions" MUST be ignored, and the creator of a "KeyPackage" object SHOULD include some random GREASE extensions to help ensure that other clients correctly ignore unknown extensions. </ins> 10.1. creator of a GroupInfo may recompute the confirmation tag as "MAC(confirmation_key, confirmed_transcript_hash)".) <ins> As discussed in extensibility, unknown extensions in "GroupInfo.extensions" MUST be ignored, and the creator of a "GroupInfo" object SHOULD include some random GREASE extensions to help ensure that other clients correctly ignore unknown extensions. Extensions in "GroupInfo.group_context.extensions", however, MUST be supported by the new joiner. </ins> New members MUST verify that "group_id" is unique among the groups they're currently participating in. advertised in it.  Otherwise, another client might fail to interoperate by selecting one of those parameters.  <ins> A client processing a KeyPackage object MUST ignore all unrecognized values in the "capabilities" field of the "LeafNode", and all unknown extensions in the "extensions" and "leaf_node.extensions" fields.  Otherwise, it could fail to interoperate with newer clients. A client processing a GroupInfo object MUST ignore all unrecognized extensions in the "extensions" field. </ins> Any field containing a list of extensions MUST NOT have more than one extension of any given type. extensions.  The "capabilities" field MUST indicate support for each extension in the GroupContext.  <ins> A client joining a group MUST verify that it supports every extension in the GroupContext for the group.  Otherwise, it should treat the enclosing GroupInfo message as invalid and not join the </ins> group.  <ins> Note that the latter two requirements mean that all MLS GroupContext extensions are mandatory, in the sense that an extension in use by the group MUST be supported by all members of the group. </ins> The parameters of a group may be changed by sending a  <ins> GroupContextExtensions proposal to enable additional extensions (groupcontextextensions), or by reinitializing the group (reinitialization). 13.5. As described in extensions, clients are required to ignore unknown values for certain parameters.  To help ensure that other clients implement this behavior, a client can follow the "Generate Random Extensions And Sustain Extensibility" or GREASE approach described in RFC8701.  In the context of MLS, this means that a client creating a LeafNode or Welcome message includes random values in certain fields, which should be ignored by a correctly-implemented client processing the message.  A client that incorrectly rejects unknown code points will fail to process such a message, providing a signal to its implementer that the client needs to be fixed. When generating the following fields, an MLS client SHOULD include a random selection of values chosen from these GREASE values: "LeafNode.capabilities.versions" "LeafNode.capabilities.ciphersuites" "LeafNode.capabilities.extensions" "LeafNode.capabilities.proposals" "LeafNode.capabilities.credentials" "KeyPackage.extensions" "GroupInfo.extensions" For the KeyPackage and GroupInfo extensions, the "extension_data" for GREASE extensions MAY have any contents selected by the sender, since they will be ignored by a correctly-implemented receiver.  For example, a sender might populate these extensions with a randomly- sized amount of random data. A set of values reserved for GREASE have been registered in the various registries in iana-considerations.  This prevents conflict between GREASE and real future values.  The following values are reserved in each registry: "0x0A0A", "0x1A1A", "0x2A2A", "0x3A3A", "0x4A4A", "0x5A5A", "0x6A6A", "0x7A7A", "0x8A8A", "0x9A9A", "0xAAAA", "0xBABA", "0xCACA", "0xDADA", and "0xEAEA".  (The value "0xFAFA" falls within the private use range.)  These values MUST only appear in the fields listed above, and not, for example, in the "proposal_type" field of a Proposal.  Clients MUST NOT implement any special processing rules for how to handle these values when receiving them, since this negates their utility for detecting extensibility failures.  GREASE values MUST be handled using normal logic for processing unsupported values.  When comparing lists of capabilities to identify mutually-supported capabilities, clients MUST represent their own capabilities with a list containing only the capabilities actually supported, without any GREASE values.  In other words, lists including GREASE values are only sent to other clients; representations of a client's own capabilities MUST NOT contain GREASE values. </ins> 14.


"leaf_node_source" is set to "key_package". If the LeafNode appears in an Update proposal, verify that <del> "leaf_node_source" is set to "update". </del>  If the LeafNode appears in the "leaf_node" value of the UpdatePath in a Commit, verify that "leaf_node_source" is set Identify the leaf L for the new member: if there are empty leaves in the tree, L is the leftmost empty leaf.  Otherwise, the tree is <del> extended to the right by one leaf node and L is the new leaf. </del>  For each non-blank intermediate node along the path from the leaf L to the root, add L's leaf index to the "unmerged_leaves" list If this is an external commit, assign the sender the leftmost blank leaf node in the new ratchet tree.  If there are no blank <del> leaf nodes in the new ratchet tree, add a blank leaf to the right side of the new ratchet tree and assign it to the sender. </del>  Update the sender's direct path in the ratchet tree as described in synchronizing-views-of-the-tree.  Define

This PR fixes small issues that I noticed while implementing proposal validation and fixing some external join bugs: The pseudocode for determining whether a path is required assumes that all proposals are by-reference. The PR updates the pseudocode to be more general, and also account for proposals by value. There were two references to adding a single leaf to the tree, which are updated to make clear that the tree grows by doubling, not by single leaves. There was no current requirement that an Update proposal have a distinct , so one is added.

"leaf_node_source" is set to "key_package". If the LeafNode appears in an Update proposal, verify that  <ins> "leaf_node_source" is set to "update" and that "encryption_key" represents a different public key than the "encryption_key" in the leaf node being replaced by the Update proposal. </ins> If the LeafNode appears in the "leaf_node" value of the UpdatePath in a Commit, verify that "leaf_node_source" is set Identify the leaf L for the new member: if there are empty leaves in the tree, L is the leftmost empty leaf.  Otherwise, the tree is  <ins> extended to the right as described in adding-and-removing-leaves and L is assigned the leftmost new blank leaf. </ins> For each non-blank intermediate node along the path from the leaf L to the root, add L's leaf index to the "unmerged_leaves" list If this is an external commit, assign the sender the leftmost blank leaf node in the new ratchet tree.  If there are no blank  <ins> leaf nodes in the new ratchet tree, expand the tree to the right as defined in adding-and-removing-leaves and assign the leftmost new blank leaf to the sender. </ins> Update the sender's direct path in the ratchet tree as described in synchronizing-views-of-the-tree.  Define


1.1. <del> MLS is designed to operate in the context described in I-D.ietf-mls- architecture.  In particular, we assume that the following services are provided: An Authentication Service (AS) that enables group members to authenticate the credentials presented by other group members. A Delivery Service (DS) that routes MLS messages among the participants in the protocol.  The following types of delivery are typically required (where KeyPackage, Proposal, Commit, and Welcome are defined below): Pre-publication of KeyPackage objects for clients Broadcast delivery of Proposal and Commit messages to members of a group Unicast delivery of Welcome messages to new members of a group Sequencing of Commit messages (see sequencing) The DS and AS may also apply additional policies to MLS operations to obtain additional security properties.  For example, MLS enables any participant to add or remove members of a group; a DS could enforce a policy that only certain members are allowed to perform these operations.  MLS authenticates all members of a group; a DS could help ensure that only clients with certain types of credential are admitted.  MLS provides no inherent protection against denial of service; A DS could also enforce rate limits in order to mitigate these risks. 1.2. </del> RFC EDITOR PLEASE DELETE THIS SECTION. draft-15 3.  The core functionality of MLS is continuous group authenticated key exchange (AKE).  As with other authenticated key exchange protocols (such as TLS), the participants in the protocol agree on a common secret value, and each participant can verify the identity of the <del> other participants.  MLS provides group AKE in the sense that there can be more than two participants in the protocol, and continuous group AKE in the sense that the set of participants in the protocol can change over time. </del>  The core organizing principles of MLS are _groups_ and _epochs_.  A group represents a logical collection of clients that share a common The creator of an MLS group creates the group's first epoch unilaterally, with no protocol interactions.  Thereafter, the members of the group advance their shared cryptographic state from one epoch <del> to another by exchanging MLS messages: </del>  A _KeyPackage_ object describes a client's capabilities and provides keys that can be used to add the client to a group. KeyPackage and Welcome messages are used to initiate a group or introduce new members, so they are exchanged between group members <del> and clients not yet in the group. </del>  Proposal and Commit messages are sent from one member of a group to the others.  MLS provides a common framing layer for sending messages 16.10.  It is possible for a malicious member of a group to "fragment" the group by crafting an invalid UpdatePath.  Recall that an UpdatePath encrypts a sequence of path secrets to different subtrees of the

This removes the following graf: The DS and AS may also apply additional policies to MLS operations to obtain additional security properties. For example, MLS enables any participant to add or remove members of a group; a DS could enforce a policy that only certain members are allowed to perform these operations. MLS authenticates all members of a group; a DS could help ensure that only clients with certain types of credential are admitted. MLS provides no inherent protection against denial of service; A DS could also enforce rate limits in order to mitigate these risks. My sense is this belongs in Security Considerations, not here.
This looks good to me. Please do move the removed paragraph to the security considerations, either here or in .

1.1.  RFC EDITOR PLEASE DELETE THIS SECTION. draft-15 3. <ins> MLS is designed to operate in the context described in I-D.ietf-mls- architecture.  In particular, we assume that the following services are provided: An Authentication Service (AS) that enables group members to authenticate the credentials presented by other group members. A Delivery Service (DS) that routes MLS messages among the participants in the protocol. MLS assumes a trusted AS but a largely untrusted DS. authentication- service-compromise describes the impact of compromise or misbehavior of an AS.  MLS is designed to protect the confidentiality and integrity of the group data even in the face of a compromised DS; in general, the DS is just expected to reliably deliver messages. delivery-service-compromise describes the impact of compromise or misbehavior of a DS. </ins> The core functionality of MLS is continuous group authenticated key exchange (AKE).  As with other authenticated key exchange protocols (such as TLS), the participants in the protocol agree on a common secret value, and each participant can verify the identity of the  <ins> other participants.  That secret can then be used to protect messages sent from one participant in the group to the other participants using the MLS framing layer or can be exported for use with other protocols.  MLS provides group AKE in the sense that there can be more than two participants in the protocol, and continuous group AKE in the sense that the set of participants in the protocol can change over time. </ins> The core organizing principles of MLS are _groups_ and _epochs_.  A group represents a logical collection of clients that share a common The creator of an MLS group creates the group's first epoch unilaterally, with no protocol interactions.  Thereafter, the members of the group advance their shared cryptographic state from one epoch  <ins> to another by exchanging MLS messages. </ins> A _KeyPackage_ object describes a client's capabilities and provides keys that can be used to add the client to a group. KeyPackage and Welcome messages are used to initiate a group or introduce new members, so they are exchanged between group members  <ins> and clients not yet in the group.  A client publishes a KeyPackage via the DS, thus enabling other clients to add it to groups.  When a group member wants to add a new member to a group it uses the new member's KeyPackage to add the new member to the group and construct a Welcome message with which the new member can initialize its local state. </ins> Proposal and Commit messages are sent from one member of a group to the others.  MLS provides a common framing layer for sending messages 16.10. <ins> The DS and AS may also apply additional policies to MLS operations to obtain additional security properties.  For example, MLS enables any participant to add or remove members of a group; a DS could enforce a policy that only certain members are allowed to perform these operations.  MLS authenticates all members of a group; a DS could help ensure that only clients with certain types of credential are admitted.  MLS provides no inherent protection against denial of service; A DS could also enforce rate limits in order to mitigate these risks. 16.11. </ins> It is possible for a malicious member of a group to "fragment" the group by crafting an invalid UpdatePath.  Recall that an UpdatePath encrypts a sequence of path secrets to different subtrees of the


"external": The signature key at the index indicated by "sender_index" in the "external_senders" group context extension (see external-senders-extension).  The "content_type" of the <del> message MUST be "proposal". </del>  "new_member_commit": The signature key in the LeafNode in the Commit's path (see joining-via-external-commits).  The A PreSharedKey proposal is invalid if any of the following is true: <del> The "psktype" in the PreSharedKeyID struct is set to "resumption" and the "usage" is "reinit" or "branch". </del>  The "psk_nonce" is not of length "KDF.Nh". includes it in the new list.) Note that once the GroupContext is updated, its inclusion in the <del> confirmation_tag by way of the key schedule will confirm that all </del>  members of the group agree on the extensions in use. 12.1.8. <del> Add and Remove proposals can be constructed and sent to the group by a party that is outside the group in two cases.  One case, indicated by an "external" SenderType is useful in cases where, for example, an automated service might propose to remove a member of a group who has been inactive for a long time, or propose adding a newly-hired staff member to a group representing a real-world team. ReInit proposals can also be sent to the group by an "external" sender, for example to enforce a changed policy regarding MLS version or ciphersuite. </del>  The "external" SenderType requires that signers are pre-provisioned to the clients within a group and can only be used if the the sender will not have the keys necessary to construct a PrivateMessage object.  12.1.8.1. The "external_senders" extension is a group context extension that example, a sender might populate these extensions with a randomly- sized amount of random data.  A set of values reserved for GREASE have been registered in the various registries in iana-considerations.  This prevents conflict between GREASE and real future values.  The following values are "proposal_type" field of a Proposal.  Clients MUST NOT implement any special processing rules for how to handle these values when receiving them, since this negates their utility for detecting <del> extensibility failures.  GREASE values MUST be handled using normal logic for processing unsupported values.  When comparing lists of capabilities to identify mutually-supported capabilities, clients MUST represent their own capabilities with a list containing only the capabilities actually supported, without any GREASE values.  In other words, lists including GREASE values are only sent to other clients; </del>  representations of a client's own capabilities MUST NOT contain GREASE values. Recommended: Same as in  Path Required: Whether a Commit covering a proposal of this type is required to have its "path" field populated (see commit).

NAME and I have completed interop testing that covers all of the functionality in the spec. We found a couple of places where there were ambiguities in the spec that led to different implementation decisions, and in the process of resolving those, found a couple of errors / omissions in drarft-19. The current PSK proposal validation forbids with other than . The branch and reinit sections call for the creation of PSK proposals with types and . Given the text in the PSK section, these proposals would be invalid. This PR explicitly allows and in the specific context of those operations. (This is a little pro-forma, since the PSK proposals are only seen by the committer. But it seems good to allow the committer to have consistent PSK proposal validation logic.) The text says that Add, Remove, and ReInit proposals can be sent by external senders, but is silent about other proposal types. This PR explicitly indicates which proposal types may be originated by external senders, and adds an IANA registry column to track. GREASE values for proposal types, credential types, and extension types are defined for use in Capabilities, but nothing currently forbids their use elsewhere. This PR forbids their use in actual Proposal or Credential objects, and in GroupContext extensions. The field of the Proposal object was inconsistently referred to as both and . This PR changes it to be everywhere.
Merge when ready.

"external": The signature key at the index indicated by "sender_index" in the "external_senders" group context extension (see external-senders-extension).  The "content_type" of the  <ins> message MUST be "proposal" and the "proposal_type" MUST be a value that is allowed for external senders. </ins> "new_member_commit": The signature key in the LeafNode in the Commit's path (see joining-via-external-commits).  The A PreSharedKey proposal is invalid if any of the following is true:  <ins> The PreSharedKey proposal is not being processed as part of a reinitialization of the group (see reinitialization), and the PreSharedKeyID has "psktype" set to "resumption" and "usage" set to "reinit". The PreSharedKey proposal is not being processed as part of a subgroup branching operation (see subgroup-branching), and the PreSharedKeyID has "psktype" set to "resumption" and "usage" set to "branch". </ins> The "psk_nonce" is not of length "KDF.Nh". includes it in the new list.) Note that once the GroupContext is updated, its inclusion in the  <ins> "confirmation_tag" by way of the key schedule will confirm that all </ins> members of the group agree on the extensions in use. 12.1.8.  <ins> Proposals can be constructed and sent to the group by a party that is outside the group in two cases.  One case, indicated by an "external" SenderType is useful in cases where, for example, an automated service might propose to remove a member of a group who has been inactive for a long time, or propose adding a newly-hired staff member to a group representing a real-world team.  An "external" sender might send a ReInit proposal, to enforce a changed policy regarding MLS version or ciphersuite. </ins> The "external" SenderType requires that signers are pre-provisioned to the clients within a group and can only be used if the the sender will not have the keys necessary to construct a PrivateMessage object. <ins> Some types of proposal cannot be sent by an "external" sender.  Among the proposal types defined in this document, only the following types may be sent by an "external" sender: "add" "remove" "psk" "reinit" "group_context_extensions" Messages from "external" senders containing proposal types other than the above MUST be rejected as malformed.  New proposal types defined in the future MUST define whether they may be sent by "external" senders.  A column is defined in the relevant IANA registry (mls- proposal-types) to reflect this property. </ins> 12.1.8.1. The "external_senders" extension is a group context extension that example, a sender might populate these extensions with a randomly- sized amount of random data. <ins> GREASE values MUST NOT be sent in the following fields, because an unsupported value in one these fields (including a GREASE value), will cause the enclosing message to be rejected: "Proposal.proposal_type" "Credential.credential_type" "GroupContext.extensions" "GroupContextExtensions.extensions" </ins> A set of values reserved for GREASE have been registered in the various registries in iana-considerations.  This prevents conflict between GREASE and real future values.  The following values are "proposal_type" field of a Proposal.  Clients MUST NOT implement any special processing rules for how to handle these values when receiving them, since this negates their utility for detecting  <ins> extensibility failures. GREASE values MUST be handled using normal logic for processing unsupported values.  When comparing lists of capabilities to identify mutually-supported capabilities, clients MUST represent their own capabilities with a list containing only the capabilities actually supported, without any GREASE values.  In other words, lists including GREASE values are only sent to other clients; </ins> representations of a client's own capabilities MUST NOT contain GREASE values. Recommended: Same as in <ins> External: Whether a proposal of this type may be sent by an "external" sender (see external-proposals). </ins> Path Required: Whether a Commit covering a proposal of this type is required to have its "path" field populated (see commit).