Draft: draft-ietf-mls-protocol

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