Change:
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> <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. <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> <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

Discussion:
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!