Draft: draft-ietf-mls-protocol

Commentary: Some clarifications and typos from my attempt to understand secret trees and the deletion schedule . Please verify that I 'm correct in that and also need to be consumed . 
 I 'll hold off on merging until we resolve the discussion above , though . 
 We currently distinguish between leaf indices and node indices , where node indices are a straight - forward index in the array representing a ratcheting tree and leaf indices index what ( I think ) used be called the roster , an array just containing the members . However , the roster was removed and instead , we always use the ratcheting tree array when referring to both nodes and leaves . As a result , whenever we use a leaf index , we have to translate it to a node index when actually indexing the array . I do n't see a reason for this complexity and propose only use node indices . This was already discussed a bit in , where pointed out that leaf indices make sure that a Remove only ever references a leaf . I think having a little check here that the index actually refers to a leaf is preferable over having to translate back and forth in many other places . Note , that we have to check anyway if the index in Remove is within the bounds of the tree , regardless of it being a leaf or a node index . I 'm happy to provide a PR if this is something we want to do . 
 When I mentioned Remove in the context of , that was only an initial example . Here 's a more fulsome list of where leaf indices are used : All of those are cases where a node index would be nonsensical . So while even with a leaf index , you still have to bounds - check , you 're basically asking for us to allow more nonsensical cases to be expressible in the syntax . As far as comprehensibility / implementability : The difference between node indices and leaf indices is clearly laid out in the , and AFAICT , all of the references to indices are clear about which one they refer to . This distinction is a good one for implementors to have in mind , and not just to simplify parsing / input validation on messages as above . There are lots of internal methods that only make sense to refer to leaves as well . For example , a TreeKEM implementation internal to an MLS implementation will need to know the index of the caller of so that it can make the right UpdatePath . Here again , providing a node index is nonsensical . So implementations will likely end up needing to differentiate leaves from nodes anyway . Net of all that , I propose closing this with no action . 
 Fair enough . Closing this , then . 
 I suggest to re - open this in order to remove the concept of lead indices from the spec . We 'll do so in OpenMLS in because it 's a footgun causing a ton of issues . If you really want to keep the concept in the spec , it must be made clear when a leaf index and when a node index is used . Taking the text for remove proposals for example . This sounds like the index is a node index . This sounds like it could be a leaf index . But it 's not defined either way . So your claim that " all of the references to indices are clear about which one they refer to . " is not true . I totally agree with this . But the current concept introduces more issues than it actually solves . So this definitely needs work , either by removing the concept ( which I 'd prefer ) or actually making clear which index is used . In my opinion the tree should only be indexed by one type of indices to reduce ambiguity and the potential for bugs . This way we can reduce the places of conversion while still making it clear to implementors that there is a distinction between the two . 
 I 'm happy to clean up references , but I would like to avoid explicitly allowing invalid requests . You should n't be able to ask to remove node 5 , or say you signed with node 7 . You risk bugs either way ; in the " all indices are node indices " case you just have to catch them at runtime , as opposed to using the right type at build time . So I continue to believe this is better handled with type systems . Nonetheless , I 'll do a review and try to clean up any ambiguous cases .
Paragraph (version A):
For the generation of encryption keys and nonces, the key schedule
begins with the "encryption_secret" and derives a tree of secrets
with the same structure as the group's ratchet tree.  Each leaf in
the Secret Tree is associated with the same group member as the
corresponding leaf in the ratchet tree.  Nodes are also assigned an
index according to their position in the array representation of the
tree (described in tree-math).  If N is a node index in the Secret
Tree then left(N) and right(N) denote the children of N (if they
exist).
Paragraph (version B):
For the generation of encryption keys and nonces, the key schedule
begins with the "encryption_secret" at the root and derives a tree of
secrets with the same structure as the group's ratchet tree.  Each
leaf in the Secret Tree is associated with the same group member as
the corresponding leaf in the ratchet tree.  Nodes are also assigned
an index according to their position in the array representation of
the tree (described in tree-math).  If N is a node index in the
Secret Tree then left(N) and right(N) denote the children of N (if
they exist).
Paragraph (version A):
Keys, nonces, and the secrets in ratchets are derived using
DeriveTreeSecret.  The context in a given call consists of the index
of the sender's leaf in the ratchet tree and the current position in
the ratchet.  In particular, the index of the sender's leaf in the
ratchet tree is the same as the index of the leaf in the Secret Tree
used to initialize the sender's ratchet.
Paragraph (version B):
Keys, nonces, and the secrets in ratchets are derived using
DeriveTreeSecret.  The context in a given call consists of the index
of the sender's leaf in the ratchet tree and the current position in
the ratchet.  In particular, the node index of the sender's leaf in
the ratchet tree is the same as the node index of the leaf in the
Secret Tree used to initialize the sender's ratchet.
Paragraph (version A):
the "init_secret", "commit_secret", "epoch_secret",
"encryption_secret" of that epoch,
Paragraph (version A):
all node secrets in the Secret Tree on the path from the root to
the leaf with index i,
Paragraph (version A):
"application_ratchet_nonce_[N]_[j]" and
"application_ratchet_nonce_[N]_[j]".
Paragraph (version A):
Then if a client uses key K1 and nonce N1 during epoch n then it must
consume (at least) values G, F, D, AR0, K1, N1 as well as the
"commit_secret" and "init_secret" used to derive G (the
"encryption_secret").  The client MAY retain (not consume) the values
K0 and N0 to allow for out-of-order delivery, and SHOULD retain AR2
for processing future messages.
Paragraph (version B):
the "commit_secret", "joiner_secret", "member_secret",
"epoch_secret", "encryption_secret" of that epoch n as well as the
"init_secret" of the previous epoch n-1,
Paragraph (version B):
all node secrets in the Secret Tree on the path from the root to
the leaf with node index N,
Paragraph (version B):
"application_ratchet_nonce_[N]_[j]" and
"application_ratchet_key_[N]_[j]".
Paragraph (version B):
Then if a client uses key K1 and nonce N1 during epoch n then it must
consume (at least) values G, F, D, AR0, AR1, K1, N1 as well as the
key schedule secrets used to derive G (the "encryption_secret"),
namely "init_secret" of epoch n-1 and "commit_secret",
"joiner_secret", "member_secret", "epoch_secret" of epoch n.  The
client MAY retain (not consume) the values K0 and N0 to allow for
out-of-order delivery, and SHOULD retain AR2 for processing future
messages.