MLSMessageContent is authenticated using the MLSMessageAuth structure. <del> The "signature" field in an MLSMessageAuth object is computed using the signing private key corresponding to the public key, which was authenticated by the credential at the leaf of the tree indicated by the sender field.  The signature is computed using "SignWithLabel" with label ""MLSMessageContentTBS"" and with a content that covers the message content and the wire format that will be used for this message.  If the sender is a member of the group, the content also covers the GroupContext for the current epoch, so that signatures are specific to a given group and epoch. </del>  The confirmation tag value confirms that the members of the group have arrived at the same state of the group. 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.  Proposals originating outside the <del> group are identified by a "preconfigured" or "new_member" SenderType in MLSPlaintext. </del>  <del> ReInit proposals can also be sent to the group by a "preconfigured" </del>  sender, for example to enforce a changed policy regarding MLS version or ciphersuite. message.  Recipients MUST verify that the MLSPlaintext carrying the Proposal message is validly signed with this key. <del> The "preconfigured" SenderType is reserved for signers that are pre- provisioned to the clients within a group.  If proposals with these sender IDs are to be accepted within a group, the members of the group MUST be provisioned by the application with a mapping between these IDs and authorized signing keys.  Recipients MUST verify that the MLSPlaintext carrying the Proposal message is validly signed with the corresponding key.  To ensure consistent handling of external proposals, the application MUST ensure that the members of a group have the same mapping and apply the same policies to external proposals. </del>  An external proposal MUST be sent as an MLSPlaintext object, since the sender will not have the keys necessary to construct an MLSCiphertext object.  13.2. A Commit message initiates a new epoch for the group, based on a Verify that the "epoch" field of the enclosing MLSMessageContent is equal to the "epoch" field of the current GroupContext object <del> Verify that the signature on the MLSMessageContent message verifies using the public key from the credential stored at the leaf in the tree indicated by the "sender" field. </del>  Verify that all PreSharedKey proposals in the "proposals" vector have unique PreSharedKeyIDs and are available.

This PR: consolidates instructions on which key should be used to compute (and verify) a signature on an MLS message depending on the sender type reverts introduces a extension that ensures that group members agree on preconfigured signature keys, as discussed in and on the mailing list
As , the current preconfigured sender mechanism isn't interoperably specified. There is no confirmation that the members of a group agree on who the allowed preconfigured senders are, so different members might respond differently. It seems like there are two options here: Add the required details. We would need a extension to confirm agreement to use the mechanism at all, and would probably need to have a list of senders there as well. Remove the feature from the base protocol. Since an extension is required in any case, there's no pressing need to have the feature in the base protocol. Personally, I would lean toward (2). Early deployment experience doesn't show a lot of need for this feature. For example, in Webex, we just have the server forward a KeyPackage instead of synthesizing an Add proposal under a preconfigured signer; the TLS authentication is as good as a preconfigured signer signature. As long as the SenderType enum is around, the extension should be a small follow-on spec if it is really needed.
NAME and I would probably prefer the first option. I will write up a more detailed reason and post to the mailing list, but the TLDR is basically that in a really long session in async messaging, server driven proposals save you from some painful logic, and the extension would improve this to allow for interop / federation cases.
I am pretty clearly in favor of option 1. Pre-configured senders are playing a key role for us in implementing automation of backend-initiated changes to the group. Things like: initiating the removal of Bob('s phone) because his account (or device) was deprovisioned. announcing a change to the group's properties due to system wide policy changes. Having an audit trail (i.e. the preconfigured sender's signature on the proposal) makes this a lot easier for the group to authenticate why this action is being committed to by group members. (Also, external commits are not a good alternative as they have the unwanted side-effect of the preconfigured sender learning the new epoch's key schedule.) As for how to agree on preconfigured sender public keys, I think a minimal solution could be to have a list of preconfigured sender public keys as a group context extension. We already have a mechanism to change it during the life time of the group (via groupcontextextensions proposal) so its a pretty small change to the the RFC.
Thanks Joël, NAME that covers my feedback as well, no need for mailing list follow up
That sounds fine to me. NAME want to write up a PR? It seems like something of the following form: The idea being that BasicCredential already has the (id, public key) pair that you would need, so you would just need to say that when , is the field from the corresponding BasicCredential. (Also probably rename to .)
Or actually, you could do things even more simply by just having the extension be a list of , and having be a index into the array.
Ok no problem. I'll take a look today and propose something and we can iterate
What do you think about it just being credential instead of basic credential?
I had the same thought. Since I kicked off the discussion, I felt the need to take a stab at closing it and I included the input from this threat and the list. See . My main goal was to consolidate the instructions on signature computation and verification and I thought I might as well deal with the preconfigured senders as well. Let me know if you want to take your own stab at the preconfigured senders extension and I'll remove that part from the PR.
Thanks NAME , glad I put this off until now :-) I'll take a look at your PR
Here's the Weekly Digest for : - - Last week 3 issues were created. Of these, 0 issues have been closed and 3 issues are still open. :greenheart: , by :greenheart: , by :greenheart: , by - - Last week, 4 pull requests were created, updated or merged. Last week, 3 pull requests were updated. :yellowheart: , by :yellowheart: , by :yellowheart: , by Last week, 1 pull request was merged. :purpleheart: , by - - Last week there was 1 commit. :hammerandwrench: by - - Last week there was 1 contributor. :bustin_silhouette: - - Last week there were no stargazers. - - Last week there were no releases. - - That's all for last week, please :eyes: Watch and :star: Star the repository to receive next weekly updates. :smiley: You can also .
Thanks for tackling this, looks good I think we are fully aligned on that. We have already departed by allowing preconfigured senders: "The preconfigured SenderType is reserved for signers that are pre-provisioned to the clients within a group. If proposals with these sender IDs are to be accepted within a group, the members of the group MUST be provisioned by the application with a mapping between these IDs and authorized signing keys. Recipients MUST verify that the MLSPlaintext carrying the Proposal message is validly signed with the corresponding key. To ensure consistent handling of external proposals, the application MUST ensure that the members of a group have the same mapping and apply the same policies to external proposals.” With preconfigured senders, it is up to the AS to verify signatures and thus to ensure authenticity. Having a new sender type for indirections would be no different. Btw, we also don’t have an extension to make sure that everyone in the group supports preconfigured senders. We just assume that this is somehow handled on the application level. So following this logic we should introduce an extension for that as well for robustness. These details aside, my main worry is that we shut the door to any (future) deniable authentication if don’t allow this indirection now. We’ve been very flexible to accommodate all sorts of AS configurations so far (BasicCredentials, X.509, preconfigured senders), why not this one? I think it is very reasonable to keep the door open, given that there has been sufficient support for this in the past. Again, any implementation that does not wish to support it is not forced to support it. In fact, I suspect that in many deployment scenarios preconfigured senders won’t be allowed either. Raphael

MLSMessageContent is authenticated using the MLSMessageAuth structure.  <ins> The signature is computed using "SignWithLabel" with label ""MLSMessageContentTBS"" and with a content that covers the message content and the wire format that will be used for this message.  If the sender's "sender_type" is "Member", the content also covers the GroupContext for the current epoch, so that signatures are specific to a given group and epoch. The sender MUST use the private key corresponding to the following signature key depending on the sender's "sender_type": "member": The signature key contained in the Credential at the leaf with the sender's "LeafNodeRef" "external": The signature key contained in the Credential at the index indicated by the "sender_index" in the "external_senders" group context extension (see Section external-senders-extension). In this case, the "content_type" of the message MUST NOT be "commit", since only members of the group or new joiners can send Commit messages. "new_member": The signature key depends on the "content_type": "proposal": The signature key in the credential contained in KeyPackage in the Add proposal (see Section external- proposals). "commit": The signature key in the credential contained in the KeyPackage in the Commit's path (see Section external- initialization). Recipients of an MLSMessage MUST verify the signature with the key depending on the "sender_type" of the sender as described above. </ins> The confirmation tag value confirms that the members of the group have arrived at the same state of the group. 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.  Proposals originating outside the  <ins> group are identified by a "external" or "new_member" SenderType in MLSPlaintext. </ins>  <ins> ReInit proposals can also be sent to the group by a "external" </ins> sender, for example to enforce a changed policy regarding MLS version or ciphersuite. message.  Recipients MUST verify that the MLSPlaintext carrying the Proposal message is validly signed with this key.  <ins> The "external" SenderType is reserved for signers that are pre- provisioned to the clients within a group.  It can only be used if the "external_senders" extension is present in the group's group context extensions. </ins> An external proposal MUST be sent as an MLSPlaintext object, since the sender will not have the keys necessary to construct an MLSCiphertext object. <ins> 13.1.9.1. The "external_senders" extension is a group context extension that contains credentials of senders that are permitted to send external proposals to the group. </ins> 13.2. A Commit message initiates a new epoch for the group, based on a Verify that the "epoch" field of the enclosing MLSMessageContent is equal to the "epoch" field of the current GroupContext object  <ins> Verify that the signature on the MLSMessageContent message as described in Section content-authentication. </ins> Verify that all PreSharedKey proposals in the "proposals" vector have unique PreSharedKeyIDs and are available.


direct path, "path_secret[1]" to the second parent node, and so on. Compute the sequence of HPKE key pairs "(node_priv,node_pub)", one for each node on the leaf's direct path, as follows. The node secret is derived as a temporary intermediate secret so that each secret is only used with one algorithm: The path secret is used as an input to DeriveSecret and the node secret is used as an input from the KDF for the group's ciphersuite, as well as the functions defined below:  The value "KDF.Nh" is the size of an output from "KDF.Extract", in bytes.  In the below diagram:

A few changes to make code blocks a little more consistent / usable / pretty: Fence every code block with exactly three tildes Mark each code block with a type ( / / / / ) Marking with generates slightly prettier pictures for the ladder diagrams in the introduction. Marking with makes it easy to programmatically extract the TLS syntax definitions in case we want to produce a syntax summary appendix like , or in case implementers want to quickly get a listing of all the structs they need. The only substantive changes I made here were: Removing the lines from the branch and re-inject PSK illustrations Splitting pseudocode from TLS syntax in a few cases.
Yeah, I specifically had the idea because I wanted to pull out the TLS syntax blocks to update my implementation :)
Nice idea! It would also allow someone to compile the document with syntax coloration for TLS formats and Python code.

direct path, "path_secret[1]" to the second parent node, and so on. Compute the sequence of HPKE key pairs "(node_priv,node_pub)", one for each node on the leaf's direct path, as follows. The node secret is derived as a temporary intermediate secret so that each secret is only used with one algorithm: The path secret is used as an input to DeriveSecret and the node secret is used as an input from the KDF for the group's ciphersuite, as well as the functions defined below: <ins> Where KDFLabel is specified as: </ins> The value "KDF.Nh" is the size of an output from "KDF.Extract", in bytes.  In the below diagram:


Added an appendix with example code for tree math  draft-00 Initial adoption of draft-barnes-mls-protocol-01 as a WG item. path.  In both cases, the path is ordered from the leaf to the root; each node MUST be the parent of its predecessor.  When using TreeKEM, the ECIESCiphertext values encoding the encrypted secret values are computed as follows: Compute the shared secret Z with the node's other child <del> Generate a fresh nonce N </del>  Encrypt the node's secret value using the AEAD algorithm specified by the ciphersuite in use, with the following inputs: <del> Key: A key derived from Z as specified by the ciphersuite </del>  <del> Nonce: A random nonce N of the size required by the algorithm </del>  Additional Authenticated Data: The empty octet string ephemeral_key: The ephemeral public key x*G <del> nonce: The random nonce N </del> ciphertext: The AEAD output Decryption is performed in the corresponding way, using the private

This matches the behavior in TLS, and saves space (due to not having to send the nonce).
Merging based on r+ from NAME out of band

Added an appendix with example code for tree math <ins> Changed the ECIES mechanism used by TreeKEM so that it uses nonces generated from the shared secret </ins> draft-00 Initial adoption of draft-barnes-mls-protocol-01 as a WG item. path.  In both cases, the path is ordered from the leaf to the root; each node MUST be the parent of its predecessor. <ins> Note that in TreeKEM, the length of the "node_secrets" vector MUST be exactly one less than the length of the "nodes" vector, since the secret value for the leaf node is not encrypted.  For "i > 0", "node_secrets[i-1]" has the encrypted node secret corresponding to the public key in "nodes[i]". </ins> When using TreeKEM, the ECIESCiphertext values encoding the encrypted secret values are computed as follows: Compute the shared secret Z with the node's other child  <ins> Derive a key and nonce as described below </ins> Encrypt the node's secret value using the AEAD algorithm specified by the ciphersuite in use, with the following inputs:  <ins> Key: The key derived from Z </ins>  <ins> Nonce: The nonce derived from Z </ins> Additional Authenticated Data: The empty octet string ephemeral_key: The ephemeral public key x*G  ciphertext: The AEAD output Decryption is performed in the corresponding way, using the private


Standards Action RFC8126.  IESG Approval is REQUIRED for a Y->N transition.  Reference: The document where this extension is defined Initial contents:

the "MLS Proposal Types" section defines a table with columns Value Name Recommended Path Required Reference But the template directly above the table only mentions four of the five columns. It does not mention or describe what "Path Required" means. It should probably at least make clear that the field should be either or , and reference section "Commit" where the path requirement is discussed.

Standards Action RFC8126.  IESG Approval is REQUIRED for a Y->N transition. <ins> Path Required: Whether a Commit covering a proposal of this type is required to have its "path" field populated (see commit). </ins> Reference: The document where this extension is defined Initial contents:


described in credentials. The "capabilities" field indicates what protocol versions, <del> ciphersuites, protocol extensions, and non-default proposal types are supported by a client.  Proposal types defined in this document are considered "default" and thus need not be listed.  Extensions that appear in the "extensions" field of a LeafNode MUST be included in the "extensions" field of the "capabilities" 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 Verify that the LeafNode is compatible with the group's parameters.  If the GroupContext has a "required_capabilities" <del> extension, then the required extensions and proposals MUST be listed in the LeafNode's "capabilities" field. </del>  Verify the "lifetime" field: when they receive the KeyPackage in an Add proposal. The field "leaf_node.capabilities" indicates what protocol versions, <del> ciphersuites, protocol extensions, and non-default proposal types are supported by the client.  (Proposal types defined in this document are considered "default" and not listed.)  This information allows MLS session establishment to be safe from downgrade attacks on the parameters described (as discussed in group-creation), while still only advertising one version / ciphersuite per KeyPackage. </del>  The field "leaf_node.leaf_node_source" of the LeafNode in a KeyPackage MUST be set to "key_package". can be imposed by including a "required_capabilities" extension in the GroupContext. <del> This extension lists the extensions and proposal types that must be supported by all members of the group.  The "default" proposal and extension types defined in this document are assumed to be implemented by all clients, and need not be listed in </del>  RequiredCapabilities in order to be safely used. For new members, support for required capabilities is enforced by 14.3.  This protocol includes a mechanism for negotiating extension parameters similar to the one in TLS RFC8446.  In TLS, extension negotiation is one-to-one: The client offers extensions in its

This PR adds a way for the members of a group to indicate the credential types they support, and how to negotiate which credential types may be used in a group.
I discussed this offline with NAME and we arrived at: We might need multiple credentials later (as I suspect) If we do that (say in an extension), we will need to update this text We should capture the general theory that covers both single- and multiple-credential cases The proposed additions here just abstract the principle of universal compatibility (which we need in a single-credential world) to one of "at least something" compatibility (which is more compatible with a multi-credential world).> The types of credential that may be used in a group is restricted to what all I think this is too restrictive if we adopt PR . I think it is between the client and its AS if all the clients need to support every credential type used in the group. If you agree with the concept, I am happy to propose slight changes.I think this makes sense.

described in credentials. The "capabilities" field indicates what protocol versions,  <ins> ciphersuites, extensions, credential types, and non-default proposal types are supported by a client.  Proposal and extension types defined in this document are considered "default" and thus need not be listed, while any credential types the application wishes to use MUST be listed.  Extensions that appear in the "extensions" field of 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" field. </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 Verify that the LeafNode is compatible with the group's parameters.  If the GroupContext has a "required_capabilities"  <ins> extension, then the required extensions, proposals, and credential types MUST be listed in the LeafNode's "capabilities" field. Verify that the credential type is supported by all members of the group, as specified by the "capabilities" field of each member's LeafNode, and that the "capabilities" field of this LeafNode indicates support for all the credential types currently in use by other members. </ins> Verify the "lifetime" field: when they receive the KeyPackage in an Add proposal. The field "leaf_node.capabilities" indicates what protocol versions,  <ins> ciphersuites, credential types, and non-default proposal/extension types are supported by the client.  (Proposal and extension types defined in this document are considered "default" and not listed.) This information allows MLS session establishment to be safe from downgrade attacks on the parameters described (as discussed in group- creation), while still only advertising one version / ciphersuite per KeyPackage. </ins> The field "leaf_node.leaf_node_source" of the LeafNode in a KeyPackage MUST be set to "key_package". can be imposed by including a "required_capabilities" extension in the GroupContext.  <ins> This extension lists the extensions, proposals, and credential types that must be supported by all members of the group.  The "default" proposal and extension types defined in this document are assumed to be implemented by all clients, and need not be listed in </ins> RequiredCapabilities in order to be safely used. For new members, support for required capabilities is enforced by 14.3. <ins> In order to ensure that MLS provides meaningful authentication it is important that each member is able to authenticate some identity information for each other member.  Identity information is encoded in Credentials, so this property is assured by ensuring that members use compatible credential types. The types of credential that may be used in a group is restricted to what all members of the group support, as specified by the "capabilities" field of each LeafNode in the RatchetTree.  An application can introduce new credential types by choosing an unallocated identifier from the registry in mls-credential-types and indicating support for the credential type in published LeafNodes, whether in Update proposals to existing groups or KeyPackages that are added to new groups.  Once all members in a group indicate support for the credential type, members can start using LeafNodes with the new credential.  Application may enforce that certain credential types always remain supported by adding a "required_capabilities" extension to the group's GroupContext, which would prevent any member from being added to the group that doesn't support them. In future extensions to MLS, it may be useful to allow a member to present more than one credential.  For example, such credentials might present different attributes attested by different authorities. To be consistent with the general principle stated at the beginning of this section, such an extension would need to ensure that each member can authenticate some identity for each other member.  For each pair of members (Alice, Bob), Alice would need to present at least one credential of a type that Bob supports.  ## Extensions </ins> This protocol includes a mechanism for negotiating extension parameters similar to the one in TLS RFC8446.  In TLS, extension negotiation is one-to-one: The client offers extensions in its


ciphersuite for a credential in a KeyPackage object.  For ciphersuites using Ed25519 or Ed448 signature schemes, the public key is in the format specified in RFC8032.  For ciphersuites using ECDSA <del> with the NIST curves P-256 or P-521, the public key is the output of the uncompressed Elliptic-Curve-Point-to-Octet-String conversion according to SECG. </del>  For an X.509 credential, each entry in the chain represents a single DER-encoded X.509 certificate.  The chain is ordered such that the

The following statement is no longer up-to-date, given addition of P-384: "For ciphersuites using ECDSA with the NIST curves P-256 or P-521, the public key is..." To avoid tracking too many changes in the future for the document, I suggest the simple edit: "For ciphersuites using ECDSA with the NIST curves, the public key is..."

ciphersuite for a credential in a KeyPackage object.  For ciphersuites using Ed25519 or Ed448 signature schemes, the public key is in the format specified in RFC8032.  For ciphersuites using ECDSA  <ins> with the NIST curves (P-256, P-384, or P-521), the public key is the output of the uncompressed Elliptic-Curve-Point-to-Octet-String conversion according to SECG. </ins> For an X.509 credential, each entry in the chain represents a single DER-encoded X.509 certificate.  The chain is ordered such that the


blank nodes that are not fixed by P's Parent Hash.  However, for each node that has an HPKE key, this key is fixed by P's Parent Hash. <del> Finally, "original_sibling_tree_hash" is the original tree hash of S. The original tree hash corresponds to the tree hash of S the last time P was updated.  It can be computed as the tree hash of S in the ratchet tree modified the following way: </del>  <del> reset the leaves in P.unmerged_leaves to blanks </del>  <del> remove P.unmerged_leaves from all unmerged_leaves lists </del>  <del> truncate the ratchet tree as described in </del>  For example, in the following tree: With P = W and S = Y, "original_sibling_tree_hash" is the tree hash of the following tree: <del> Because "W.unmerged_leaves = [H]", H is removed from "Y.unmerged_leaves", then H is replaced with a blank leaf, then the tree is truncated removing the last two nodes. With P = W and S = U, "original_sibling_tree_hash" is the tree hash of the following tree: This time we have 4 leaf nodes because the truncation of the ratchet tree didn't remove the last leaf. </del>  Note that no recomputation is needed if the tree hash of S is unchanged since the last time P was updated.  This is the case for as long as the intersection of the parent's unmerged leaves with the subtree is the same as in the earlier computation. <del> Observe that "original_child_resolution" is equal to the resolution of S at the time the "UpdatePath" was generated, since at that point P's set of unmerged leaves was emptied.  (Observe also that "original_child_resolution" contains all unmerged leaves of S.) Therefore, P's Parent Hash fixes, for each node V on the path from P to the root, not only the HPKE public key of V, but also the set of HPKE public keys to which the corresponding HPKE secret key of V was encrypted by the generator of the "UpdatePath". </del> 8.8.1. The Parent Hash of P appears in three types of structs.  If V is

The current draft implements the original incorrect version of the tree-hash-for-parent-hash mechanism proposed in . That version . This PR implements the corrected "full-extension + tree hash" mechanism proposed by NAME .
In order to use tree hashes to represent siblings in parent hashes, we had to compute a different tree hash on the right edge of the tree, pretending that the tree is full, in the sense of having leaves. Thus we actually have two tree hashes in play, one used for group agreement on the tree and one used for parent hash inputs. We should consider specifying that the logical tree used by MLS is always full. This change to the specification would not change the computations done by the protocol much, if at all. Thanks to the fact that we no longer generate redundant nodes (), there is never anything non-blank to the right of the rightmost non-blank leaf anyway. So the main impact is to insert some blank nodes between a parent node on the right edge of the tree and its right child -- and all of the algorithms in the specification already skip over such nodes. Likewise, implementations' internal represntations of the tree wouldn't have to change much, since the extra nodes could be "virtual". The only time they would be noticed is when computing tree hashes. We would have to update the description of the extension to say that it only covers the tree up to the rightmost non-blank leaf. It would simplify the specification in a few ways, for example: The "original sibling tree hash" would just entail blanking unmerged leaves, not structure changes. The description of extending and truncating the tree would be much simpler. The main cost would be extra hashes when joining, or when updating a member close to the right edge of the tree. If you have a tree with leaves, then you have to compute tree hashes over blank leaves and their blank parents.
Thanks to NAME and NAME for helping inspire and develop this idea in discussions around
Virtual interim 2022-05-19 NAME notes that the current tree hash is quite tricky to implement, so this would be a useful simplification Doubling the hashes on a fresh tree doesn't sound too intimidating Folks would find more concreteness useful here, interested in PR Could we phrase this as only a change to TreeKEM? Would be premised on the equivalence of the hashed tree an the tree you operate on
Discussed at 19 May interim: NAME to draft a PR.
Holding a PR until after lands. Some notes on what would need to change: {{ratchet-tree-terminology}} would change from "the unique left-balanced tree with N leaves" to "the unique balanced binary tree with depth , where is the smallest number such that ". {{adding-and-removing-leaves}} would either be removed, or updated so that it talks about increasing or decreasing the depth of the tree (equivalently, doubling or halving the number of leaves). {{add}} would talk about increasing depth instead of adding leaves. {{remove}} would talk about decreasing depth instead of removing leaves. {{ratchet-tree-extension}} would be explicit that it only carries nodes up to the rightmost non-blank leaf. The encoding/decoding algorithms would have to be updated. {{array-based-trees}} and {{link-based-trees}} could probably be simplified. We would need to make sure all figures showed full trees.
I went ahead and filed for this, based on top of . Once that merges, we can update the PR to be a little easier to read. The changes are basically as described above. One additional note: We have actually already incurred the cost to a new joiner of hashing all the extra nodes, because they have to do it for parent hash verification anyway. So this approach is actually a net reduction in the amount of hashing that has to be done, since they don't have to also compute the non-full-tree hashes.
Thanks for finding yet more parent-hash issues NAME :) I was initially concerned about this approach, because it seemed like it would be expensive to fill out the tree. However, (a) we only incur that cost for the nodes on the right edge of the tree, and (b) the extra nodes to the right are "virtual", in the sense that they don't take up any memory, they just cause some extra hashes. I and it was pretty straightforward (that also allowed me to verify that it fixes the pathological case that NAME notes). Overall, this looks good to merge once we get the noted nits fixed.

blank nodes that are not fixed by P's Parent Hash.  However, for each node that has an HPKE key, this key is fixed by P's Parent Hash.  <ins> Finally, "original_sibling_tree_hash" is the tree hash of S in the ratchet tree modified as follows: </ins>  <ins> Extend the subtree of S by adding blank leaves until it is full, i.e., until its number of leaves is a power of 2 (see adding-and- removing-leaves). </ins>  <ins> For each leaf L in "P.unmerged_leaves", blank L and remove it from the "unmerged_leaves" sets of all parent nodes. </ins>  <ins> Observe that "original_sibling_tree_hash" does not change between updates of P.  This property is crucial for the correctness of the protocol. </ins> For example, in the following tree: With P = W and S = Y, "original_sibling_tree_hash" is the tree hash of the following tree:  <ins> Because "W.unmerged_leaves" includes F, F is blanked and removed from "Y.unmerged_leaves". </ins> Note that no recomputation is needed if the tree hash of S is unchanged since the last time P was updated.  This is the case for as long as the intersection of the parent's unmerged leaves with the subtree is the same as in the earlier computation.  8.8.1. The Parent Hash of P appears in three types of structs.  If V is


Terminology specific to tree computations is described in ratchet- tree-terminology.  2.1. We use the TLS presentation language RFC8446 to describe the A number of values are derived from the epoch secret for different purposes: <del> (In general, we use "secret" to refer to a value that is used derive further secret values, and "key" to refer to a value that is used with an algorithm such as HMAC or an AEAD algorithm.) </del> The "external_secret" is used to derive an HPKE key pair whose private key is held by the entire group: 9.7. <del> The main MLS key schedule provides a per-epoch "authentication_code". If one of the parties is being actively impersonated by an attacker, their "authentication_code" will differ from that of the other group members.  Thus, members of a group MAY verify out of band that their "authentication_code" values are all the same, in order to ensure that they share the same view of the group. </del>  10.

That looks like a good change and clarification. I added a couple small changes in in-line comments above.
Thanks for the review NAME and thanks for hashing through this.
The latest edit looks good and that works.
Reopening due to incomplete discussion and inaccuracy. The claims presented are incorrect in response by NAME demonstrate a misunderstanding of the roles of these keys and uses, and constitutes a change to the fundamental uses thereof. The authenticationsecret is NOT a public value nor was it ever introduced as such. If an application is using it as a public value, that onus is on the developers, and I advise against using Cisco's implementation as a standard for MLS. As shown in the name and all original discussion, the authenticationsecret is intended as a secret value (or else 'public' in the same way that the group secret is 'public). It is used for further derivation to support authentication. This should be corrected, and such foundational changes without WG discussion should be avoided.
Clearly we have a disagreement here NAME but maybe let's hold off on attributing misunderstanding. It seems like you are claiming that in order for MLS to be secure, the needs to be protected to the same level as the . What is this based on? If that is true, then we should probably remove the altogether. The "out-of-band" protocol in which it would be confirmed would have to be part of the MLS security analysis, thus no longer out-of-band.
NAME that is not the claim at all - different uses imply different compromise risks. This should not be a new concept to understand. As demonstrated in the terminology, the authenticationsecret is linked to authentication, not confidentiality. MLS, per specification, outsources authentication (the AS is not explicitly defined and all entity authentication within MLS relies on it). Even though it is definitionally out-of-scope and therefore out-of-band (certificates, authentication verification are entirely out-of-band of the protocol spec), we do not say that signature secret keys can be "public" - yet you are asserting that an authenticationsecret which can support epoch-level authentication would be public. The authenticationsecret is private in the same way that the exportervalue is: based on key schedule derivation, compromise of these keys should not undermine MLS, but secrecy of them can be leveraged. So, unless you are now proposing removing mention of certificates, etc. from the core protocol, there really isn't a logical basis for removing 'out-of-band' authentication mechanisms.
What I'm trying to get at here is: What is the negative consequence if an is known by someone other than the members of a group? For example, leaking a signature private key would enable the attacker to impersonate the corresponding member. Leaking the would allow the attacker to decrypt all of the group's messages. What is the bad consequence of an becoming public?
NAME I am also assuming that this push to use epoch-level authentication values as public codes is based on Cisco's implementation choice. I would be curious to know why there is such insistence that all implementations also go about using authentication in the same way. If an implementation chooses to use it as a secret value to then derive an authentication code based on yet other information, why is that a problem?
Publishing the authenticationsecret as a public value has a similar effect as publishing the exportervalue as public - it is not an issue for MLS per protocol spec, but it would definitely be an issue for the application if it is then used for something secret thereafter. One reason not to specify these as public values is that they are derived following the conditions placed on keys and therefore could be used as secret values if desired by an application. Forcing or annotating them as immediately public, or as final 'codes', when such a use as a secret key is an out-of-scope potential is artificial and unnecessary.
I'm not pushing anything, I'm just trying to get the protocol to be clear. If this thing needs to be secret, then it needs to be secret, and we need to document that so that people keep it secret. Thanks for elaborating more on your concerns. I see what you're saying -- basically the same point that NAME and I discussed . I think the core of the disagreement here is what we mean when we say that a value is "public" or "secret". I mean something very specific: That the security guarantees of MLS will not be harmed if a public value is provided to the attacker, and they will be harmed if a secret value is provided to the attacker. Importantly, "public" does not mean that the value must be published, or that the security guarantees of an application using MLS will not be harmed if you publish the value. It seems to me that in that specific taxonomy, the is "public" and not "secret". Do you disagree?
BTW, the notion of something being "public" at one layer but "secret" at another is not new. For example, values exported from TLS aren't secret in the sense that publishing them would compromise TLS. You could authenticate your TLS connection by exporting a value and shouting it through megaphones across a crowd. But the main way people use them in practice is as secrets in some other protocol. Every WebRTC call uses SRTP keys that are exported from TLS, and many people would be very sad if these "public" values were published.
If it would help, I would be happy to add some text to the Security Considerations clarifying this distinction. Basically, draw a clear boundary around which values have to be kept confidential to the client in order to realize the security properties of the protocol. But I still think is a clearer name. Doesn't mean you have to use it in any particular way in your application; doesn't prevent it being used as a shared secret; but also doesn't imply that the MLS library has to worry about its secrecy.
Within the bounds of that particular taxonomy there is not an issue. However, while you may be following a given taxonomy, that is not a general one, one that would be widely understood by implementors, nor is it specified in the protocol spec. It is not an unreasonable taxonomy, but it certainly not a general one. If you are looking for MLS usage spec to align to your taxonomy then I do think that there should be a clarification section added. BTW as a point case on more standard terminology use, the TLS 1.3 spec uses the exact phrasing for exporters as "exportermastersecret", from which the actual exporter may be derived. This aligns very closely to the authenticationsecret, from which an authentication code later may be derived. TLS is generic in 'exporters' after an exportermastersecret is derived. Thus there is alignment of the term 'secret' to things that can be used for keys (have sufficient entropy), not as an alignment to public/secret at different layers. I am strongly opposed to the use of authenticationcode, and see no clear definition for that in your customized taxonomy. As such, it would follow the more standard interpretations, such as message authentication codes, etc. which are indeed explicitly public values. Codes are normally an end-state in themselves, not keys. Perhaps it is a clearer name for the Cisco implementation design, but it is not a clear name for something that can/should be used for further derivations as may be more generally applicable. Following the TLS norm for exporters, the name would be "authenticationmastersecret". The shortened "authenticationsecret" was a good name. If you are going to provide a write-up for the custom taxonomy, then an adjustment to "authenticationexporter" could perhaps be made.
The notion of a having boundary to the protocol and values that may or may not safely be exported across the boundary is not exotic or customized. You see it in FIPS 140-2, PKCS, and WebCrypto, and in tools like ProVerif and Tamarin. The only choice I'm making is in the specific words, in particular reserving "secret" for "something that should not be exposed outside the protocol implementation because it would compromise the protocol". I hope you agree that is different from say in this regard. It would be OK for an MLS implementation to have an API for an application to access the , but it would not be OK to have an API to access the . I'm not wedded to , I just want something other than . If you want to align with the exported values (to which I agree it is similar), we could call it an .
The distinction between those documents and tools, and the MLS spec is that those tools clearly define the boundaries and terminology, vs. just anyone making terminology assumptions without them being specified in the document. Not to be pedantic, but if you want to assume specific meanings for terms relative to a threat model for MLS only, versus a general usage ("exporter secrets" being secrets used in other things), then you really do need to specify those assumptions within the specification itself and not various side discussions. Such specification is part of what a standard is meant to do. Within other documents this is clearly defined - i.e., what is known as a threat model - such as one sees in ProVerif and Tamarin. Assuming that you are going to add such a taxonomy, and that it follows that described by you in , then the correct term would be "authenticationkey". To quote you what you said there, which correctly aligns to a reasonable usage of the authenticationkey. pointed out that there is a lack of consistency in the usage of "keys" vs "secrets" throughout the document - something that still has not been addressed and should not be dropped from the issue for edits. Regardless of what taxonomy is being used or even assumed, there is no consistency in it in the spec. You seem to be commenting on adjustments only for the "key" and "secret" uses that appear as variables, but the commentary within the spec interchangeably use these terms for the same thing. Whatever is used should be applied consistently. It is not reasonable to assume that developers will correctly guess reasons for terminology separations particularly when they are inconsistently enforced.
On your latter point about usage of "keys" vs "secrets" throughout the document — Sorry if I misinterpreted your intent here. If you're seeing places where there are inconsistencies, it would be helpful if you could highlight those more specifically, or ideally send a PR to fix them.
NAME had a call about this today and hashed out a couple of things: We should be clear in general about what we mean by secrets/keys, in that we generally use them interchangeably, but sometimes use one or the other to indicate how a specific value is used The need for secrecy of the value labeled depends on the context in which it is used. So we should provide some text to suggest that there are different ways to use it, and use a fairly neutral name. NAME suggested , which I like a lot. I have attempted to implement these in

Terminology specific to tree computations is described in ratchet- tree-terminology. <ins> In general, symmetric values are referred to as "keys" or "secrets" interchangeably.  Either term denotes a value that MUST be kept confidential to a Client.  When labelling individual values, we typically use "secret" to refer to a value that is used derive further secret values, and "key" to refer to a value that is used with an algorithm such as HMAC or an AEAD algorithm. </ins> 2.1. We use the TLS presentation language RFC8446 to describe the A number of values are derived from the epoch secret for different purposes:  The "external_secret" is used to derive an HPKE key pair whose private key is held by the entire group: 9.7.  <ins> The main MLS key schedule provides a per-epoch "epoch_authenticator". If one member of the group is being impersonated by an active attacker, the "epoch_authenticator" computed by their client will differ from those computed by the other group members. This property can be used to construct defenses against impersonation attacks that are effective even if members' signature keys are compromised.  As a trivial example, if the users of the clients in an MLS group were to meet in person and reliably confirm that their epoch authenticator values were equal (using some suitable user interface), then each user would be assured that the others were not being impersonated in the current epoch.  As soon as the epoch changed, though, they would need to re-do this confirmation.  The state of the group would have changed, possibly introducing an attacker. More generally, in order for the members of an MLS group to obtain concrete authentication protections using the "epoch_authenticator", they will need to use it in some secondary protocol (such as the face-to-face protocol above).  The details of that protocol will then determine the specific authentication protections provided to the MLS group. </ins> 10.


To enforce forward secrecy and post-compromise security of messages, each participant periodically updates its leaf secret which represents its contribution to the group secret.  Any member of the <del> group can send an Update at any time by generating fresh leaf secret and keys and send an Update message that describes how to update the </del>  group secret with that new information.  Once all participants have processed this message, the group's secrets will be unknown to an attacker that had compromised the sender's prior leaf secret. _siblings_ if they share the same parent. A _subtree_ of a tree is the tree given by the descendants of any <del> node, the _head_ of the subtree The _size_ of a tree or subtree is </del>  the number of leaf nodes it contains.  For a given parent node, its _left subtree_ is the subtree with its left child as head (respectively _right subtree_). The _direct path_ of a root is the empty list, and of any other node is the concatenation of that node with the direct path of its parent. The _copath_ of a node is the list of siblings of nodes in its direct <del> path, excluding the root, which has no sibling.  The _frontier_ of a tree is the list of heads of the maximal full subtrees of the tree, ordered from left to right. </del>  For example, in the below tree:

NAME Hi Brendan ! Would you mind please revert the last of the 3 changes. Even though the sentence is not amazing and your change correct, I find it better to keep explicit the fact that the root should not be included, instead of having to infer it from the fact it has no siblings :) Thanks !
You don't have to infer it -- direct paths are defined to never include the root node in the sentence just before.

To enforce forward secrecy and post-compromise security of messages, each participant periodically updates its leaf secret which represents its contribution to the group secret.  Any member of the  <ins> group can send an Update at any time by generating a fresh leaf secret and sending an Update message that describes how to update the </ins> group secret with that new information.  Once all participants have processed this message, the group's secrets will be unknown to an attacker that had compromised the sender's prior leaf secret. _siblings_ if they share the same parent. A _subtree_ of a tree is the tree given by the descendants of any  <ins> node, the _head_ of the subtree.  The _size_ of a tree or subtree is </ins> the number of leaf nodes it contains.  For a given parent node, its _left subtree_ is the subtree with its left child as head (respectively _right subtree_). The _direct path_ of a root is the empty list, and of any other node is the concatenation of that node with the direct path of its parent. The _copath_ of a node is the list of siblings of nodes in its direct  <ins> path.  The _frontier_ of a tree is the list of heads of the maximal full subtrees of the tree, ordered from left to right. </ins> For example, in the below tree:


RFC EDITOR PLEASE DELETE THIS SECTION.  draft-13 TLS syntax updates (including variable-header-length vectors) (*)

This should have been done before publishing draft-14, but we can't fix that now. At least this will capture the history in draft-15.
Merging my own PR because this is strictly editorial.

RFC EDITOR PLEASE DELETE THIS SECTION. <ins> draft-14 Ensure that a signature public key is always intelligible (*) Clean up terminology of derived secrets/keys Fix parent hash (*) Specify compatibility behavior around new credentials Add Path Required to Proposal Type template Sub-group branching requires fresh key packages for each member Use "aasvg" and typed code blocks Require init key and leaf key to be different Preconfigured senders extension and removal of signature key indirection </ins> draft-13 TLS syntax updates (including variable-header-length vectors) (*)


involved in the group can change from one epoch to the next, and MLS ensures that only the members in the current epoch have access to the epoch secret.  From the epoch secret, members derive further shared <del> secrets for message encryption, group membership authentication, etc. </del>  The creator of an MLS group creates the group's first epoch unilaterally, with no protocol interactions.  Thereafter, the members A _key schedule_ that describes the chain of key derivations used to progress from epoch to epoch (mainly using the _init_secret_ <del> and _epoch_secret_); and to derive a variety of other secrets (see epoch-derived-secrets) used during the current epoch.  One of these (the _encryption_secret_) is the root of _secret_tree_. </del>  A _secret tree_ derived from the key schedule that represents <del> shared secrets used by the members of the group to provide confidentiality and forward secrecy for MLS messages.  Each epoch has a distinct secret tree. </del>  <del> Each member of the group maintains a view of these facets of the group's state.  MLS messages are used to initialize these views and keep them in sync as the group transitions between epochs. </del>  Each new epoch is initiated with a Commit message.  The Commit instructs existing members of the group to update their views of the ratchet tree by applying a set of Proposals, and uses the updated ratchet tree to distribute fresh entropy to the group.  This fresh <del> entropy is provided only to members in the new epoch, not to members who have been removed, so it maintains the confidentiality of the epoch secret (in other words, it provides post-compromise security with respect to those members). For each Commit that adds member(s) to the group, there is a single corresponding Welcome message.  The Welcome message provides all the new members with the information they need to initialize their views of the key schedule and ratchet tree, so that these views are equivalent to the views held by other members of the group in this </del>  epoch. <del> In addition to defining how one epoch secret leads to the next, the key schedule also defines a collection of secrets that are derived from the epoch secret.  For example: An _encryption secret_ that is used to initialize the secret tree for the epoch. A _confirmation key_ that is used to confirm that all members agree on the shared state of the group. A _resumption secret_ that members can use to prove their membership in the group, e.g., in the case of branching a subgroup. Finally, an _init secret_ is derived that is used to initialize the next epoch. </del> 4.2. There are three major operations in the lifecycle of a group: the group can send a Remove proposal followed by a Commit message. The Commit message provides new entropy to all members of the group except the removed member.  This new entropy is added to the epoch <del> secret for the new epoch, so that it is not known to the removed </del>  member.  Note that this does not necessarily imply that any member is actually allowed to evict other members; groups can enforce access control policies on top of these basic mechanism. 4.3. A group has a single linear sequence of epochs.  Groups and epochs <del> are generally independent of one-another.  However, it can sometimes </del>  be useful to link epochs cryptographically, either within a group or across groups.  MLS derives a resumption pre-shared key (PSK) from each epoch to allow entropy extracted from one epoch to be injected

Some of these editorial comments I find less clear than the original. The code changes and varint are fine. Could you please split this into two more atomic PRs so we can discuss separately?
Feel free to leave comments on the editorial portion of this PR to keep things moving

involved in the group can change from one epoch to the next, and MLS ensures that only the members in the current epoch have access to the epoch secret.  From the epoch secret, members derive further shared  <ins> secrets for message encryption, group membership authentication, and so on. </ins> The creator of an MLS group creates the group's first epoch unilaterally, with no protocol interactions.  Thereafter, the members A _key schedule_ that describes the chain of key derivations used to progress from epoch to epoch (mainly using the _init_secret_  <ins> and _epoch_secret_), as well as the derivation of a variety of other secrets (see epoch-derived-secrets), for example: An _encryption secret_ that is used to initialize the secret tree for the epoch. An _exporter secret_ that allows other protocols to leverage MLS as a generic authenticated group key exchange. A _resumption secret_ that members can use to prove their membership in the group, e.g., in the case of branching a subgroup. </ins> A _secret tree_ derived from the key schedule that represents  <ins> shared secrets used by the members of the group for encrypting and authenticating messages.  Each epoch has a distinct secret tree. </ins>  <ins> Each member of the group maintains a partial view of these components of the group's state.  MLS messages are used to initialize these views and keep them in sync as the group transitions between epochs. </ins> Each new epoch is initiated with a Commit message.  The Commit instructs existing members of the group to update their views of the ratchet tree by applying a set of Proposals, and uses the updated ratchet tree to distribute fresh entropy to the group.  This fresh  <ins> entropy is provided only to members in the new epoch and not to members who have been removed.  Commits thus maintain the property that the the epoch secret is confidential to the members in the current epoch. For each Commit that adds one or more members to the group, there is a single corresponding Welcome message.  The Welcome message provides all the new members with the information they need to initialize their views of the key schedule and ratchet tree, so that these views align with the views held by other members of the group in this </ins> epoch.  4.2. There are three major operations in the lifecycle of a group: the group can send a Remove proposal followed by a Commit message. The Commit message provides new entropy to all members of the group except the removed member.  This new entropy is added to the epoch  <ins> secret for the new epoch so that it is not known to the removed </ins> member.  Note that this does not necessarily imply that any member is actually allowed to evict other members; groups can enforce access control policies on top of these basic mechanism. 4.3. A group has a single linear sequence of epochs.  Groups and epochs  <ins> are generally independent of one another.  However, it can sometimes </ins> be useful to link epochs cryptographically, either within a group or across groups.  MLS derives a resumption pre-shared key (PSK) from each epoch to allow entropy extracted from one epoch to be injected


The signature is computed using "SignWithLabel" with label ""MLSMessageContentTBS"" and with a content that covers the message content and the wire format that will be used for this message.  If <del> the sender's "sender_type" is "Member", the content also covers the GroupContext for the current epoch, so that signatures are specific to a given group and epoch. </del>  The sender MUST use the private key corresponding to the following signature key depending on the sender's "sender_type": <del> "member": The signature key contained in the Credential at the index indicated by "leaf_index" in the ratchet tree. </del>  <del> "external": The signature key contained in the Credential at the index indicated by "sender_index" in the "external_senders" group context extension (see Section external-senders-extension).  In this case, the "content_type" of the message MUST NOT be "commit", since only members of the group or new joiners can send Commit messages. </del>  <del> "new_member": The signature key depends on the "content_type": "proposal": The signature key in the credential contained in KeyPackage in the Add proposal (see Section external- proposals). "commit": The signature key in the credential contained in the KeyPackage in the Commit's path (see Section external- initialization). </del>  Recipients of an MLSMessage MUST verify the signature with the key depending on the "sender_type" of the sender as described above. The confirmation tag value confirms that the members of the group <del> have arrived at the same state of the group. A MLSMessageAuth is said to be valid when both the "signature" and "confirmation_tag" fields are valid. </del>  7.2. 13.1.8. Add and Remove proposals can be constructed and sent to the group by <del> a party that is outside the group.  For example, a Delivery 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.  Proposals originating outside the group are identified by a "external" or "new_member" SenderType in MLSPlaintext. </del>  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 "new_member" SenderType is used for clients proposing that they themselves be added.  For this ID type the sender value MUST be zero and the Proposal type MUST be Add. The MLSPlaintext MUST be signed with the private key corresponding to the KeyPackage in the Add message.  Recipients MUST verify that the MLSPlaintext carrying the Proposal message is validly signed with this key. The "external" SenderType is reserved for signers that are pre- provisioned to the clients within a group.  It can only be used if the "external_senders" extension is present in the group's group context extensions. </del>  An external proposal MUST be sent as an MLSPlaintext object, since the sender will not have the keys necessary to construct an

Sending an Add proposal from outside the group is how we let people do "external add" functionality without needing to publish the group state. But you can't talk right away like you can with an external Commit. In that case, applications seem more inclined to build this functionality themselves so let's just remove it from the spec.
Virtual interim 2022-05-19: External sender change is unrelated NAME notes that using only SignaturePublicKey loses auth info NAME to revert this part of the PR Clear to merge once conflicts are cleared and above is reverted
Once SignaturePublicKey is backed out and merge conflicts fixed - merge.
NAME raised a few points today, which I'm trying to capture in this issue with some thoughts on proposed solutions. In , we removed the usage of to send Add proposals, in order to allow for the signature on an external commit to cover the GroupContext (which is not necessarily possible for an external Add sender). In the discussion, the point was made that an Add proposal only really differs from a KeyPackage in that the KeyPackage doesn't have or fields. Part of the resolution in was a decision that folks didn't think was a significant loss. And I think there's pretty broad agreement that is actively undesirable, since it means that the new joiner has to be sufficiently in sync with the group to know the epoch hasn't changed before sending its proposal. After talking with NAME there are two other issues to highlight: Losing is a bigger deal than it might first seem in a system that has both "prepublished KeyPackages" and "KeyPackage as request to be added". Someone could go grab a bunch of prepublished KeyPackages that they don't own, and send them in as requests to be added. This isn't an immediate security risk, since the attacker doesn't necessarily have the private keys that go with the KeyPackages (if they do, they have other attack avenues). But it would mean, for example, that a client would have an even less certain idea of all the groups it was a member of. When "requests to be added" were expressed as Proposals, the "request" semantic followed from being a Proposal, which tied into the recommendations for what should be included in a Commit. Expressing these requests in a different way, means that the "request" semantic rests outside of the core MLS logic. A few possible avenues come to mind for addressing these issues: Don't change the protocol, but document the operational considerations. For example: The "unintended join" issue with abuse of prepublished KPs could be mitigated by a DS that supports proactive joins enforcing that the sender owns the signature key (e.g., checking that authenticates the same identity that the DS has for the client). The request semantic could be captured in the architecture document / protocol overview as a thing that applications could implement if they choose. Re-add joiner-sent Add proposals, using a different SenderType this time to avoid the conflict with external commits. Say and This would still have the epoch problem. Add a field to KeyPackage indicating whether the KeyPackage is general or directed to a specific group Something like Require that if the field is set in an Add proposal, then it MUST match the group's ID. Specify that only directed KPs can be used to request to be added Add parallel text to proposals that recommend the the committer commit Add proposals for any directed KPs it has received. Define a new top level message (an entry in the MLSMessage enum) that expresses precisely the semantic we want, something like:
Thanks NAME for writing this up. My problems with using KeyPackages as a "verb" are more extensive than what Richard described. The rules for deciding which combinations of Proposals are valid are already pretty complicated. Relying on a new implicit mechanism that interacts with this explicit Proposal logic makes me very uncomfortable. While it might require some retries to get the correct epoch, I am also concerned about copy paste attacks. I think approaches 1, 3, and 4 are all vulnerable some form of copy paste attack. I propose re-adding new joiner-sent Add Proposals with a new SenderType. While catching the correct epoch can be a nuisance, I think it is a separable problem. Thanks, -rohan
Personally, I'm less concerned about the flow concerns here and more concerned about epoch mismatches. In real-time scenarios, epoch mismatches are a huge pain. You have potentially hundreds of people joining at the beginning of a meeting and retrying a join adds a lot of latency. So the ability for the committer to choose which requests to join go in which Commits is really important for things to work smoothly. I would not be able to use (2) in Webex, but would gladly use (3). As for being more implicit: I actually have the opposite concern, that allowing joiner-signed Adds is riskier from a policy POV. In general, before someone is added to the group, some application logic at a member needs to check that the new member is acceptable. If an Add proposal has to be made by a member, you get two opportunities to check, when the proposal is made and when it's committed. If the new joiner makes the proposal, you lose the first. (I also have a more minor aesthetic concern about re-using the general framing for this specific niche use case. We have this general framing that is actually general for members; they can send any content type. External proposals and external commits are constrained to one content type, but still pretty general. New member add proposals would be limited to one content type and one proposal type, which is getting quite narrow.) By contrast, the semantic shift for extending KeyPackage is really pretty minor. The semantic of KeyPackage is already "Please make an Add proposal with this object to add me to a group". That's not far away from "Please make an Add proposal to add me to ${THISSPECIFICGROUP}." That seems like a smaller semantic shift than the shift from "Someone authorized to make changes to this group proposed a change" to "Some rando on the Internet proposed a change". Net of all that, I'm pretty strongly in favor of (3).
NAME I specifically mentioned copy paste attacks. I would be happy for us to have a reasonable general solution to epoch mismatches within a narrow time window, but not at the expense of an unbounded timeframe for a copy paste attack. That said, if you want to implement some implicit KeyPackage behavior to add using authorization rules that work for your implementation and your DS is in a position to prevent those attacks, you can still do what you proposed. I'm just asking not to take away the tool we are using. In terms of scope, external add proposals have been in the protocol document since -08 (2019). We use them extensively and implemented them in good faith. Removing them now and proposing we use an implicit approach with a know security concern that happens to solve a problem that was previously out of scope seems a bit arbitrary. Proposal 2 (new SenderType) does not harm anyone else and preserves functionality in the draft. It does not prevent you from solving the epoch problem your way or in any other future way.
I think the points raised by NAME are quite valid, in particular since external proposals can still be used with preconfigured senders (and there is a better symmetry with external remove proposals). I don't see why that wouldn't be good enough in a scenario where the DS is quite involved beyond the handshake message ordering anyway. That being said, from all the options above (3) would also be my favorite. I like the idea of purpose-bound KeyPackages.
I don't understand this concern since adding someone to a group has never required their consent or knowledge. The change here is just that you can perhaps get someone else to do the adding, though I would say that's better in the sense that a non-malicious client doing the Add would actually send the Welcome. If the malicious user is doing the Add, they might not send a Welcome. NAME Would you mind expanding more on what you mean by copy-paste attack? This isn't a downside, this is a core reason that it external Adds were removed. We observed that applications seem to prefer doing this themselves (at least those that spoke on the ticket / interim did).
The concern with implying Add semantics using a bare (undirected) keypackage is we don't have any record of anyone's intent to add the MLS client to the group. [I don't understand what is supposedly "better" about not sending an external proposal. Either way someone sends a Proposal and whoever sends a valid commit containing that Proposal is supposed to send the Welcome.] Alice (in domain A) decides to join the Bananas group in domain B. Alice sends a directed KeyPackage (option 3). Variation 1: Two members of the Bananas group, Cathy and Doug, copy this request and replay it repeatedly to the group, possibly after Alice leaves the group. [Currently we only will forward a Welcome to an MLS client if the KeyPackageRef was seen when consuming a KeyPackage from the Directory; or from sending an External Proposal or External Commit in that epoch. So before this change we could scope the state needed to forward a Welcome to a specific sender, or a particular epoch. Either way this was easy to clean up. With the current proposal this needs to be sent across all the domains in the group, and is no longer time bounded.] Variation 2: An attacker Bob on B returns an error and copies (stored) the add request. Later at a time of Bob's choosing, he sends (pastes) the request to the Bananas group, causing Alice to be added to Bananas at a different time than Alice intended. Well, we are an implementer and are definitely using them and like them, so it is a downside for us, and it sounds like for everyone else it was a no-op, not an upside (they can do it their way whether or not External Proposals for Adds are possible or not). For background, we are using External Proposal Adds only to add a new client device representing a user who is already in the group. We never use them to add a different user or for a server to add a user. Our clients are not necessarily online at the same time, so there may not be an MLS member online when the request is sent. This is fine for External Proposal because each client can process and verify MlsMessage linearly. In addition, all our use cases are federated, so the user who ends up sending the Commit which finally adds the new client often has no direct relationship with the backend/directory through which the new client normally forwards MlsCiphertext/MlsPlaintext messages or in which is stores its KeyPackages.
On the copy/paste risk: It occurs to me that we actually have a solution for this already: The field in LeafNode. KeyPackages are already designed to expire, and that mechanism provides flexibility that matches the use case much better than .
Our use case for external add proposals is for an ordinary client adding itself (never a preconfigured sender), whereas for external removes this is always a backend taking action (preconfigured sender). >I don't see why that wouldn't be good enough in a scenario where the DS is quite involved beyond the handshake message ordering anyway. I'm not sure I follow you. Coordinating enough DS involvement across federated domains looks hard to me. Could you elaborate please?
Good point, although running a bunch of timers on servers is definitely more work than checking the epoch.
So in that instance you say it is fine to use preconfigured senders, why wouldn't it be fine when adding members? Distributing keys is indeed a hard problem in a federated environment. But I don't see how that's getting better with "new member" external Add proposals. In both cases, there is a signature to be verified and the key material needs to be available to the verifier.
One more note on the preconfigured senders: They are synchronized as a group extension (something that was added recently and I forgot about it). That makes the distribution much easier and less error-prone than the external Add with sender type "new member".
Interim 2022-05-26: A few comparisons: Add vs. Add ~ Add includes client's intent to join Add vs. directed KeyPackage ~ main difference is epoch vs. lifetime NAME to make a PR implementing the "new sender type" approach NAME to make a PR implementing the "directed KeyPackage" approach Aim to decide on next call
Looks good, thanks for putting this together!

The signature is computed using "SignWithLabel" with label ""MLSMessageContentTBS"" and with a content that covers the message content and the wire format that will be used for this message.  If  <ins> the sender's "sender_type" is "member", the content also covers the GroupContext for the current epoch so that signatures are specific to a given group and epoch. </ins> The sender MUST use the private key corresponding to the following signature key depending on the sender's "sender_type":  <ins> "member": The signature key contained in the LeafNode at the index indicated by "leaf_index" in the ratchet tree. </ins>  <ins> "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 message MUST be "proposal". </ins>  <ins> "new_member": The signature key in the LeafNode in the Commit's path (see joining-via-external-commits).  The "content_type" of the message MUST be "commit". </ins> Recipients of an MLSMessage MUST verify the signature with the key depending on the "sender_type" of the sender as described above. The confirmation tag value confirms that the members of the group  <ins> have arrived at the same state of the group.  An MLSMessageAuth is said to be valid when both the "signature" and "confirmation_tag" fields are valid. </ins> 7.2. 13.1.8. Add and Remove proposals can be constructed and sent to the group by  <ins> a party that is outside the group, indicated by an "external" SenderType.  This 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. </ins> 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.  <ins> The "external" SenderType requires that signers are pre-provisioned to the clients within a group and can only be used if the "external_senders" extension is present in the group's GroupContext. </ins> An external proposal MUST be sent as an MLSPlaintext object, since the sender will not have the keys necessary to construct an


8.4. <del> In order to provide forward secrecy and post-compromise security, whenever a member initiates an epoch change (i.e., commits; see commit), they refresh the key pairs of their leaf and of all nodes on their leaf's direct path (all nodes for which they know the secret key). </del>  The member initiating the epoch change generates the fresh key pairs using the following procedure.  The procedure is designed in a way group are identified by a "external" or "new_member" SenderType in MLSPlaintext. <del> ReInit proposals can also be sent to the group by a "external" </del>  sender, for example to enforce a changed policy regarding MLS version or ciphersuite.

In first paragraph, the "Ratchet Tree Evolution" Section states that whenever a member initiates an epoch change, they refresh the key pair in their leaves, etc. This is not the case for partial commits, where the epoch is advanced without a key update and without achieving PCS.

8.4.  <ins> Whenever a member initiates an epoch change (i.e., commits; see commit), they may need to refresh the key pairs of their leaf and of the nodes on their leaf's direct path in order to maintain forward secrecy and post-compromise security. </ins> The member initiating the epoch change generates the fresh key pairs using the following procedure.  The procedure is designed in a way group are identified by a "external" or "new_member" SenderType in MLSPlaintext.  <ins> ReInit proposals can also be sent to the group by an "external" </ins> sender, for example to enforce a changed policy regarding MLS version or ciphersuite.


13.1.8.1. The "external_senders" extension is a group context extension that <del> contains credentials of senders that are permitted to send external proposals to the group. </del>  13.2.

The External Senders Extension allows clients to agree on the entities that are allowed to send external proposals, as well as the signature public keys required to verify them. It contains a list of credentials that is meant to achieve this goal. However, due to a recent change, Credentials don't necessarily contain signature public keys anymore. NAME suggested to have it contain a signature public key, as well as a corresponding Credential, which allows members to verify the binding between signature public key and the owning entity's identity, which I think would be a good solution.
Thanks NAME I like that this captures both the signature and authentication aspects. NAME wdyt?Looks good to me! NAME idea might be a good additional improvement.

13.1.8.1. The "external_senders" extension is a group context extension that  <ins> contains the credentials and signature keys of senders that are permitted to send external proposals to the group. </ins> 13.2.


key that can be used to encrypt to that client, and which other clients can use to introduce the client to a new group.  A signing key pair used to authenticate the sender of a message. An MLSPlaintext or MLSCiphertext message carrying an MLS Proposal

There are several references to GroupContext in the first few sections. The details are fine where they are, but it would be good to introduce the notion that GroupContext summarizes the state of the group.

key that can be used to encrypt to that client, and which other clients can use to introduce the client to a new group. <ins> An object that summarizes the state of the group.  The group context is signed to bind a message to a particular group, and also provided to new members to help them join a group. </ins> A signing key pair used to authenticate the sender of a message. An MLSPlaintext or MLSCiphertext message carrying an MLS Proposal


16.2. During each epoch senders MUST NOT encrypt more data than permitted <del> by the security bounds of the AEAD scheme used. </del>  Note that each change to the Group through a Handshake message will also set a new "encryption_secret".  Hence this change MUST be

In the "Restrictions" section, we say "During each epoch senders MUST NOT encrypt more data than permitted by the security bounds of the AEAD scheme used", without specifying the limits. We should cite something here, probably (cc NAME for visibility).

16.2. During each epoch senders MUST NOT encrypt more data than permitted  <ins> by the security bounds of the AEAD scheme used I-D.irtf-cfrg-aead- limits. </ins> Note that each change to the Group through a Handshake message will also set a new "encryption_secret".  Hence this change MUST be