Draft: draft-ietf-mls-protocol

Commentary: I have managed to implement Proposal / Commit in mlspp , but it required some modifications to the Welcome and Commit structs and processes . Two major things to point out : This PR proposes to send the same DirectPath to both new members and current members . This avoids the need to have two code paths and special tree math to implant path secrets , at the cost of some bloat in the Welcome and Commit messages . As a result , the sequencing of GroupContexts is a bit tricky . The GroupContext used for encrypting / decrypting  is an intermediate context -- the new group context , but before the transcript is updated . All that said , this version is . The implementation exercise highlighted that the net effect of Proposal / Commit was to isolate basically all of the state management complexity to three methods in the client stack : Processing of Welcome messages Generation of Commit+Welcome messages Processing of Commit messages This PR reflects the algorithms for those three things . 
 I one minor change ( order of fields ) and rebased . I think this is ready to merge . I 'm also about to drop offline for a bit , so please pull the trigger if it looks good to you . 
  , I ’ll take a look in tonight or tomorrow morning and merge it . 
 I am merging this now because it includes fixes we definitely need even though I note that I have one philosophical concern and one security concern ... the numbers of transcript hashes in the state is an implementation concern and not a protocol concern , I would much prefer keep only the real ( confirmed ) transcript hash . ( The implementation should keep the current state and one provisional updated state but the struct should n't have to keep both ) the initsecret and the updatesecret at once seem equivalent to sending the epoch secret but I do n't think it is actually the case since it could be rejected by the DS , breaking the encryption key of that message would hence still have consequences even if the message is rejected . I 'll open an issue for that . 
 Here 's the Weekly Digest for : - - Last week 8 issues were created . Of these , 0 issues have been closed and 8 issues are still open . : greenheart : , by : greenheart : , by : greenheart : , by : greenheart : , by : greenheart : , by : greenheart : , by : greenheart : , by : greenheart : , by : speaker : , by It received 1 comments . - - Last week , 6 pull requests were created , updated or merged . Last week , 1 pull request was opened . : greenheart : , by Last week , 3 pull requests were updated . : yellowheart : , by : yellowheart : , by : yellowheart : , by Last week , 2 pull requests were merged . : purpleheart : , by : purpleheart : , by - - Last week there were 10 commits . : hammerandwrench : by : hammerandwrench : by : hammerandwrench : by : hammerandwrench : by : hammerandwrench : by : hammerandwrench : by : hammerandwrench : by : hammerandwrench : by : hammerandwrench : by : hammerandwrench : by - - Last week there were 2 contributors . : bustinsilhouette : : bustin_silhouette : - - Last week there was 1 stargazer . : star : You are the star ! : star2 : - - Last week there were no releases . - - That 's all for last week , please < kbd : eyes : Watch</kbdand < kbd : star : Star</kbdthe repository to receive next weekly updates . : smiley : You can also . 
 Here 's the Weekly Digest for : - - Last week 4 issues were created . Of these , 0 issues have been closed and 4 issues are still open . : greenheart : , by : greenheart : , by : greenheart : , by : greenheart : , by - - Last week , 14 pull requests were created , updated or merged . Last week , 8 pull requests were updated . : yellowheart : , by : yellowheart : , by : yellowheart : , by : yellowheart : , by : yellowheart : , by : yellowheart : , by : yellowheart : , by : yellowheart : , by Last week , 6 pull requests were merged . : purpleheart : , by : purpleheart : , by : purpleheart : , by : purpleheart : , by : purpleheart : , by : purpleheart : , by - - Last week there were 10 commits . : hammerandwrench : by : hammerandwrench : by : hammerandwrench : by : hammerandwrench : by : hammerandwrench : by : hammerandwrench : by : hammerandwrench : by : hammerandwrench : by : hammerandwrench : by : hammerandwrench : by - - Last week there were 3 contributors . : bustinsilhouette : : bustinsilhouette : : bustinsilhouette : - - Last week there were no stargazers . - - Last week there were no releases . - - That 's all for last week , please < kbd : eyes : Watch</kbdand < kbd : star : Star</kbdthe repository to receive next weekly updates . : smiley : You can also . 
 Ok , this looks good to me . I think we do n't have to derive that many keys in the main KS but that 's ok for now , we can optimize later .
Paragraph (version A):
Every node, regardless of whether the node is blank or populated, has
a corresponding _hash_ that summarizes the contents of the subtree
below that node.  The rules for computing these hashes are described
in tree-hashes.
Paragraph (version B):
Every node, regardless of whether the node is blank or populated, has
a corresponding _hash_ that summarizes the contents of the subtree
below that node.  The rules for computing these hashes are described
in tree-hashes-and-signatures.
Paragraph (version A):
The reverse implication is not true: A member may not know the
private keys of all the intermediate nodes they're below.  Such a
member has an _unmerged_ leaf.  Encrypting to an intermediate node
requires encrypting to the node's public key, as well as the public
keys of all the unmerged leaves below it.  A leaf is unmerged when it
is first added, because the process of adding the leaf does not give
it access to all of the nodes above it in the tree.  Leaves are
"merged" as they receive the private keys for nodes, as described in
ratchet-tree-updates.
Paragraph (version B):
The reverse implication is not true: A member may not know the
private keys of all the intermediate nodes they're below.  Such a
member has an _unmerged_ leaf.  Encrypting to an intermediate node
requires encrypting to the node's public key, as well as the public
keys of all the unmerged leaves below it.  A leaf is unmerged when it
is first added, because the process of adding the leaf does not give
it access to all of the nodes above it in the tree.  Leaves are
"merged" as they receive the private keys for nodes, as described in
ratchet-tree-evolution.
Paragraph (version A):
The "tree_hash" field contains a commitment to the contents of the
group's ratchet tree and the credentials for the members of the
group, as described in tree-hashes.
Paragraph (version B):
The "tree_hash" field contains a commitment to the contents of the
group's ratchet tree and the credentials for the members of the
group, as described in tree-hashes-and-signatures.
Paragraph (version A):
As described in ratchet-tree-commits, each MLS message needs to
transmit a ClientInitKey leaf and node values along its direct path.
The path contains a public key and encrypted secret value for all
intermediate nodes in the path above the leaf.  The path is ordered
from the closest node to the leaf to the root; each node MUST be the
parent of its predecessor.
Paragraph (version B):
As described in commit, each MLS Commit message needs to transmit a
ClientInitKey leaf and node values along its direct path.  The path
contains a public key and encrypted secret value for all intermediate
nodes in the path above the leaf.  The path is ordered from the
closest node to the leaf to the root; each node MUST be the parent of
its predecessor.
Paragraph (version A):
Each proposal covered by the Commit is identified by a ProposalID
structure.  The "sender" field in this structure indicates the member
of the group that sent the proposal (according to their index in the
ratchet tree).  The "hash" field contains the hash of the
MLSPlaintext in which the Proposal was sent, using the hash function
for the group's ciphersuite.
Paragraph (version B):
Each proposal covered by the Commit is identified by a ProposalID
value, which contains the hash of the MLSPlaintext in which the
Proposal was sent, using the hash function for the group's
ciphersuite.
Paragraph (version B):
Construct an initial Commit object with "updates", "removes",
"adds", and "ignored" fields populated from Proposals received
during the current epoch, and an empty "path" field.
Paragraph (version B):
Generate a provisional GroupContext object by applying the
proposals referenced in the initial Commit object in the order
provided, as described in proposals.  Add proposals are applied
left to right: Each Add proposal is applied at the leftmost
unoccupied leaf, or appended to the right edge of the tree if all
leaves are occupied.
Paragraph (version B):
Create a DirectPath using the new tree (which includes any new
members).  The GroupContext for this operation uses the
"group_id", "epoch", "tree", and "prior_confirmed_transcript_hash"
values in the initial GroupInfo object.
Paragraph (version B):
Assign this DirectPath to the "path" fields in the Commit and
GroupInfo objects.
Paragraph (version B):
Construct an MLSPlaintext object containing the Commit object.
Use the "commit_secret" to advance the key schedule and compute
the "confirmation" value in the MLSPlaintext.  Sign the
MLSPlaintext using the current epoch's GroupContext as context.
Paragraph (version B):
For each new member in the group, compute an EncryptedKeyPackage
object that encapsulates the "init_secret" for the current epoch.
Construct a Welcome message from the encrypted GroupInfo object
and the encrypted key packages.
Paragraph (version A):
The "commit_secret" is the value "path_secret[n+1]" derived
from the "path_secret[n]" value associated with the root node.
Paragraph (version B):
Update the new GroupContexts confirmed and interim transcript
hashes using the new Commit.
Paragraph (version A):
Decrypt the "encrypted_key_package" using HPKE with the algorithms
indicated by the ciphersuite and the HPKE public key in the
ClientInitKey.
Paragraph (version A):
Decrypt the "encrypted_group_info" field using the key and nonce
in the decrypted KeyPackage object.
Paragraph (version B):
Decrypt the "encrypted_key_package" using HPKE with the algorithms
indicated by the ciphersuite and the HPKE private key
corresponding to the ClientInitKey.
Paragraph (version B):
From the "init_secret" in the decrypted KeyPackage object, derive
the "group_info_secret", "group_info_key", and "group_info_nonce".
Use the key and nonce to decrypt the "encrypted_group_info" field.
Paragraph (version A):
Construct a new group state using the information in the GroupInfo
object and verify the integrity of the tree using the "tree_hash".
The new member's position in the tree is "index", as defined
above.
Paragraph (version A):
Identify the lowest node at which the direct paths from "index"
and "signer_index" overlap.  Set private keys for that node and
its parents up to the root of the tree, using the "path_secret"
from the KeyPackage and following the algorithm in ratchet-tree-
commits to move up the tree.
Paragraph (version B):
Construct a new group state using the information in the GroupInfo
object.  The new member's position in the tree is "index", as
defined above.  In particular, the confirmed transcript hash for
the new state is the "prior_confirmed_transcript_hash" in the
GroupInfo object.
Paragraph (version B):
Use the "init_secret" from the KeyPackage object together with the
decrypted "commit_secret" to generate the epoch secret and other
derived secrets for the current epoch.
Paragraph (version B):
Set the confirmed transcript hash in the new state to the value of
the "confirmed_transcript_hash" in the GroupInfo.
Paragraph (version B):
Verify the confirmation MAC in the GroupInfo using the derived
confirmation key and the "confirmed_transcript_hash" from the
GroupInfo.