jiebi/TextFlow huggingface dataset:
Original text:
find out the plaintext by correlation between the question and the length. <del> Similarly to TLS 1.3, if padding is used, the MLS messages MUST be padded with zero-valued bytes before AEAD encryption.  Upon AEAD decryption, the length field of the plaintext is used to compute the number of bytes to be removed from the plaintext to get the correct data.  As the padding mechanism is used to improve protection against traffic analysis, removal of the padding SHOULD be implemented in a "constant-time" manner at the MLS layer and above layers to prevent timing side-channels that would provide attackers with information on the size of the plaintext.  The padding length length_of_padding can be chosen at the time of the message encryption by the sender. Recipients can calculate the padding size from knowing the total size of the ApplicationPlaintext and the length of the content. </del>  14.2.

Reviewer comments:
The padding section was out of date based on prior changes.
In section 14.1 Message Encryption and Decryption there is a paragraph describing the reasons why you would want to use padding, and part of that mentions the following As the padding mechanism is used to improve protection against traffic analysis, removal of the padding SHOULD be implemented in a "constant-time" manner at the MLS layer and above layers to prevent timing side-channels that would provide attackers with information on the size of the plaintext. The padding length lengthofpadding can be chosen at the time of the message encryption by the sender. This seems out of date given that padding is a separate field that doesn't need to be explicitly stripped off at decrypt time and there is no other mention of in the document.
That seems right. We used to have a more clever, more economical padding scheme mirroring what TLS does. But that got rationalized a while ago. These are probably dangling references. Want to send a PR?
Sounds good, pushed PR URL

Revised text:
find out the plaintext by correlation between the question and the length.  <ins> The content and length of the "padding" field in "MLSCiphertextContent" can be chosen at the time of message encryption by the sender.  It is recommended that padding data is comprised of zero-valued bytes and follows an established deterministic padding scheme. </ins> 14.2.
Original text:
The Messaging Layer Security (MLS) Protocol <del> draft-barnes-mls-protocol-latest </del>  Abstract RFC EDITOR: PLEASE REMOVE THE FOLLOWING PARAGRAPH The source for this draft is maintained in GitHub.  Suggested changes should be submitted <del> as pull requests at https://github.com/ekr/mls-protocol. </del>  Instructions are on that page as well.  Editorial changes can be managed in GitHub, but any substantive change should be discussed on the MLS mailing list.

Reviewer comments:
Note: If you have an existing clone, you will need to delete at the root level of the clone in order for to work. ( will still work fine)

Revised text:
The Messaging Layer Security (MLS) Protocol  <ins> draft-ietf-mls-protocol-latest </ins> Abstract RFC EDITOR: PLEASE REMOVE THE FOLLOWING PARAGRAPH The source for this draft is maintained in GitHub.  Suggested changes should be submitted  <ins> as pull requests at https://github.com/mlswg/mls-protocol. </ins> Instructions are on that page as well.  Editorial changes can be managed in GitHub, but any substantive change should be discussed on the MLS mailing list.
Original text:
"index" field in "PSKLabel" corresponds to the index of the PSK in the "psk" array, while the "count" field contains the total number of PSKs.  In other words, the PSKs are chained together with KDF.Extract <del> invocations, as follows: </del>  In particular, if there are no PreSharedKey proposals in a given Commit, then the resulting "psk_secret" is "psk_secret_[0]", the all-

Reviewer comments:
Adjust ASCII art to fit within 72 character limit. Did not fix tables or code in appendices.

Revised text:
"index" field in "PSKLabel" corresponds to the index of the PSK in the "psk" array, while the "count" field contains the total number of PSKs.  In other words, the PSKs are chained together with KDF.Extract  <ins> invocations (labelled "Extract" for brevity in the diagram), as follows: </ins> In particular, if there are no PreSharedKey proposals in a given Commit, then the resulting "psk_secret" is "psk_secret_[0]", the all-
Original text:
state with other clients.  A client is defined by the cryptographic keys it holds. <del> A linear sequence of epochs in which each epoch depends on its </del>  predecessor. A state of a group in which a specific set of authenticated

Reviewer comments:
The definition of an MLS group in the 2nd paragraph of the Protocol Overview is really good. Let's use it as the definition in the Terminology section as well.

Revised text:
state with other clients.  A client is defined by the cryptographic keys it holds.  <ins> A group represents a logical collection of clents that share a common secret value at any given time.  Its state is represented as a linear sequence of epochs in which each epoch depends on its </ins> predecessor. A state of a group in which a specific set of authenticated
Original text:
A node in the tree may also be _blank_, indicating that no value is present at that node.  The _resolution_ of a node is an ordered list of non-blank nodes that collectively cover all non-blank descendants <del> of the node.  The resolution of a node is effectively a depth-first, left-first enumeration of the nearest non-blank nodes below the node: </del>  The resolution of a non-blank node comprises the node itself, followed by its list of unmerged leaves, if any

Reviewer comments:
In the most basic case, the resolution of a node is the node, so let's say that.
Fwiw this is stated immediately below your note:
Bren2010 said: >Fwiw this is stated immediately below your note: Yes. And it took me three readings to understand that the most complicated scenario was being described first and the most basic scenario is not explicitly stated. Most readers will not understand in what cases there will be unmerged leaves by this point.
Looks good with one minor edit.

Revised text:
A node in the tree may also be _blank_, indicating that no value is present at that node.  The _resolution_ of a node is an ordered list of non-blank nodes that collectively cover all non-blank descendants  <ins> of the node.  The resolution of a non-blank node with no unmerged leaves is just the node itself.  More generally, the resolution of a node is effectively a depth-first, left-first enumeration of the nearest non-blank nodes below the node: </ins> The resolution of a non-blank node comprises the node itself, followed by its list of unmerged leaves, if any
Original text:
efficiently encrypt messages to subsets of the group.  Each epoch has a distinct ratchet tree. <del> Each member of the group maintains a view of these two facets of the </del>  group's state.  MLS messages are used to initialize these views and keep them in sync as the group transitions between epochs. key schedule also defines a collection of secrets that are derived from the epoch secret.  For example: <del> An _encryption secret_ that is used to initialize a _secret tree_, which provides keys for encrypting handshake and application messages and providing forward secrecy for these messages within an epoch. </del>  A _confirmation key_ that is used to confirm that all members agree on the shared state of the group.

Reviewer comments:
In the Protocol Overview, Figure 1 shows a ratchet tree, commitsecret, epochsecret, encryptionsecret, and a secret tree, while the text discusses the epochsecret and encryption_secret. Then the next section, Cryptographic State and Evolution states that "There are two types of cryptographic state at the core of MLS:" a key schedule, and a ratchet tree. The Secret Tree isn't mentioned there. Then we have a Key Schedule section which then includes a Secret Tree sub-section. Readers are left wondering what the relationship is between the the Key Schedule and Secret Tree.

Revised text:
efficiently encrypt messages to subsets of the group.  Each epoch has a distinct ratchet tree.  <ins> A _secret tree_ derived from the key schedule that represents 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. Each member of the group maintains a view of these facets of the </ins> group's state.  MLS messages are used to initialize these views and keep them in sync as the group transitions between epochs. key schedule also defines a collection of secrets that are derived from the epoch secret.  For example:  <ins> An _encryption secret_ that is used to initialize the secret tree for the epoch. </ins> A _confirmation key_ that is used to confirm that all members agree on the shared state of the group.
Original text:
4.1. <del> There are two types of cryptographic state at the core of MLS: A _key schedule_ that represents the shared secret state of the group and its evolution from one epoch to the next. </del>  A _ratchet tree_ that represents the membership of the group, providing group members a way to authenticate each other and efficiently encrypt messages to subsets of the group.  Each epoch <del> has a distinct ratchet tree. </del>  A _secret tree_ derived from the key schedule that represents shared secrets used by the members of the group to provide

Reviewer comments:
Note that is currently under , so that should probably move or the top level section should be renamed. I ran out of imagination for today.

Revised text:
4.1.  <ins> The cryptographic state at the core of MLS is divided into three areas of responsibility: </ins> A _ratchet tree_ that represents the membership of the group, providing group members a way to authenticate each other and efficiently encrypt messages to subsets of the group.  Each epoch  <ins> has a distinct ratchet tree.  It seeds the _key schedule_. A _key schedule_ that describes the chain of key derivations used to progress from epoch to epoch (mainly using the _init_secret_ 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_. </ins> A _secret tree_ derived from the key schedule that represents shared secrets used by the members of the group to provide
Original text:
Verify that the credential in the KeyPackage is valid according to the authentication service and the client's local policy.  These actions MUST be the same regardless of at what point in the <del> protocol the KeyPackage is being verified. </del>  Verify that the signature on the KeyPackage is valid using the public key in the KeyPackage's credential Verify that the "hpke_init_key" value is different from the corresponding field in the KeyPackage being replaced. <del> Verify that the set of identities attested by the credential is acceptable to the application for the participant being updated. </del> Replace the sender's leaf KeyPackage with the one contained in the Update proposal

Reviewer comments:
This PR moves the remark that there has to be validation of the attested identities upon a credential update to the KeyPackage validation subsection. It also changes the responsibility for the check to be that of the AS rather than that of the application. Note, that the boundaries are somewhat arbitrary at this point, since the application running on the local device may well implement parts of the AS. The reason for this change is to ensure any validation checks that pertain to authentication are part of the AS.
NAME Please make any required changes to the architecture document as well. Since I think that's where the AS is defined.
Right now it says in the Update proposal section: "Verify that the set of identities attested by the credential is acceptable to the application for the participant being updated." Firstly, I think this is a matter of authentication and thus the AS' job and secondly, it's a validation step that should probably go into the section.
Discussion on virtual interim: Depends on what the boundary of the AS is: Checking credential is valid Also checking (credential -> credential) transitions Moving this check to KP validation section would make it clearer NAME to make a PR
Fixed by

Revised text:
Verify that the credential in the KeyPackage is valid according to the authentication service and the client's local policy.  These actions MUST be the same regardless of at what point in the  <ins> protocol the KeyPackage is being verified with the following exception: If the KeyPackage is an update to another KeyPackage, the authentication service MUST additionally validate that the set of identities attested by the credential in the new KeyPackage is acceptable relative to the identities attested by the old credential. </ins> Verify that the signature on the KeyPackage is valid using the public key in the KeyPackage's credential Verify that the "hpke_init_key" value is different from the corresponding field in the KeyPackage being replaced.  Replace the sender's leaf KeyPackage with the one contained in the Update proposal
Original text:
In this process, the joiner sends a new "init_secret" value to the group using the HPKE export method.  The joiner then uses that <del> "init_secret" with information provided in the PublicGroupState and an external Commit to initialize their copy of the key schedule for the new epoch. </del>  Members of the group receive the "kem_output" in an ExternalInit proposal and preform the corresponding calculation to retrieve the "init_secret" value. <del> In both cases, the "info" input to HPKE is set to the PublicGroupState for the previous epoch, encoded using the TLS serialization. </del>  9.4.

Reviewer comments:
PublicGroupState was removed as an independent struct in . This PR fixes some dangling references to it.
cc NAME
Comment by NAME Hmmm :P
Cf. RFC 8446 We could provide some more citations to the analyses that are now in the literature if you think that would help.
CC NAME NAME NAME for citations
They are also in this architecture document PR: URL
Oh good, I'll just add a citation over to mls-architecture.
Also, update WireFormat: s/mlspublicgroupstate(4),/mlsgroup_info(4),/

Revised text:
In this process, the joiner sends a new "init_secret" value to the group using the HPKE export method.  The joiner then uses that  <ins> "init_secret" with information provided in the GroupInfo and an external Commit to initialize their copy of the key schedule for the new epoch. </ins> Members of the group receive the "kem_output" in an ExternalInit proposal and preform the corresponding calculation to retrieve the "init_secret" value.  <ins> In both cases, the "info" input to HPKE is set to the GroupInfo for the previous epoch, encoded using the TLS serialization. </ins> 9.4.
Original text:
The parent hash of P changes whenever an "UpdatePath" object is applied to the ratchet tree along a path from a leaf U traversing <del> node V (and hence also P).  The new "Parent Hash of P (with Co-Path Child S)" is obtained by hashing P's "ParentHashInput" struct. </del>  The field "public_key" contains the HPKE public key of P.  If P is the root, then the "parent_hash" field is set to a zero-length octet If, on the other hand, V is the leaf U and its LeafNode has "leaf_node_source" set to "commit", then the Parent Hash of P (with <del> V's sibling as co-path child) is stored in the "parent_hash" field. </del>  This is true in particular of the LeafNode object sent in the "leaf_node" field of an UpdatePath.  The signature of such a LeafNode thus also attests to which keys the group member introduced into the

Reviewer comments:
The co-path is written both as 'co-path' and 'copath'. As a formality check, this should be unified.

Revised text:
The parent hash of P changes whenever an "UpdatePath" object is applied to the ratchet tree along a path from a leaf U traversing  <ins> node V (and hence also P).  The new "Parent hash of P (with copath child S)" is obtained by hashing P's "ParentHashInput" struct. </ins> The field "public_key" contains the HPKE public key of P.  If P is the root, then the "parent_hash" field is set to a zero-length octet If, on the other hand, V is the leaf U and its LeafNode has "leaf_node_source" set to "commit", then the Parent Hash of P (with  <ins> V's sibling as copath child) is stored in the "parent_hash" field. </ins> This is true in particular of the LeafNode object sent in the "leaf_node" field of an UpdatePath.  The signature of such a LeafNode thus also attests to which keys the group member introduced into the
Original text:
concatenating the resolution of its left child with the resolution of its right child, in that order <del> For example, consider the following tree, where the "_" character </del>  represents a blank node and unmerged leaves are indicated in square brackets: The resolution of leaf 1 is the empty list [] <del> The resolution of root node is the list [A, Z, C] </del>  Every node, regardless of whether the node is blank or populated, has a corresponding _hash_ that summarizes the contents of the subtree to DeriveKeyPair. For example, suppose there is a group with four members, with C an <del> unmerged leaf at node 5: </del>  If member B subsequently generates an UpdatePath based on a secret "leaf_secret", then it would generate the following sequence of path Compute the updated path secrets. <del> Identify a node in the filtered direct path for which the local member is in the subtree of the non-updated child. </del>  Identify a node in the resolution of the copath node for which <del> this node has a private key. </del>  Decrypt the path secret for the parent of the copath node using the private key from the resolution node.

Reviewer comments:
This PR adds two clarifications suggested by NAME Clarifying some antecedents in UpdatePath processing Explaining how the example trees can come about in practice

Revised text:
concatenating the resolution of its left child with the resolution of its right child, in that order  <ins> For example, consider the following subtree, where the "_" character </ins> represents a blank node and unmerged leaves are indicated in square brackets: The resolution of leaf 1 is the empty list []  <ins> The resolution of top node is the list [A, Z, C] </ins> Every node, regardless of whether the node is blank or populated, has a corresponding _hash_ that summarizes the contents of the subtree to DeriveKeyPair. For example, suppose there is a group with four members, with C an  <ins> unmerged leaf at Z: </ins> If member B subsequently generates an UpdatePath based on a secret "leaf_secret", then it would generate the following sequence of path Compute the updated path secrets.  <ins> Identify a node in the filtered direct path for which the recipient is in the subtree of the non-updated child. </ins> Identify a node in the resolution of the copath node for which  <ins> the recipient has a private key. </ins> Decrypt the path secret for the parent of the copath node using the private key from the resolution node.
Original text:
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:

Reviewer comments:
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.

Revised text:
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:
Original text:
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

Reviewer comments:
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

Revised text:
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