representation of the state of the group by applying the proposals and advancing the key schedule. <del> A group member that has observed one or more Proposal messages within an epoch MUST send a Commit message before sending application data. This ensures, for example, that any members whose removal was proposed during the epoch are actually removed before any application information is transmitted. The sender of a Commit message MUST include in it all valid Proposals that the sender has received during the current epoch.  Invalid Proposals include, for example, Proposals with an invalid signature or Proposals that are semantically inconsistent, such as a Remove proposal for an unoccupied leaf.  The Commit MUST NOT combine Proposals sent within different epochs.  Despite these requirements, it is still possible for a valid Proposal not to be covered by a Commit, for example, because the sender of the Commit did not receive the Proposal.  In such cases, the sender of the proposal can retransmit the Proposal in the new epoch.  In the case where a committer is processing Proposals where an Update proposal or a Remove proposal exists for herself, this proposal MUST be ignored and added to the list of discarded proposals in the Commit. </del> Each proposal covered by the Commit is identified by a ProposalID value, which contains the hash of the MLSPlaintext in which the <del> Proposal was sent, using the hash function for the group's </del>  ciphersuite. <del> The sender of a Commit message MUST include in it all proposals that it has received during the current epoch.  Proposals that recipients should implement are placed in the "updates", "removes", and "adds" vector, according to their type.  Proposals that should not be implemented are placed in the "ignored" vector.  For example, if two Update proposals are issued for the same leaf, then one of them (presumably the earlier one) should be ignored and the other (presumably the later) should be added to the "updates" vector. </del>  [[ OPEN ISSUE: This structure loses the welcome_info_hash, because new participants are no longer expected to have access to the Commit A member of the group creates a Commit message and the corresponding Welcome message at the same time, by taking the following steps: <del> 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. </del>  Generate a provisional GroupContext object by applying the proposals referenced in the initial Commit object in the order Regardless of how messages are kept in sequence, implementations MUST only update their cryptographic state when valid Commit messages are <del> received.  Generation of handshake messages MUST be stateless, since the endpoint cannot know at that time whether the change implied by the handshake message will succeed or not. </del>  11.1. With this approach, the delivery service ensures that incoming messages are added to an ordered queue and outgoing messages are <del> dispatched in the same order.  The server is trusted to resolve conflicts during race-conditions (when two members send a message at the same time), as the server doesn't have any additional knowledge thanks to the confidentiality of the messages. </del>  Messages should have a counter field sent in clear-text that can be checked by the server and used for tie-breaking.  The counter starts

Ignored proposals don't make sense to me. How can they possibly add value? Also, re-structured section on Commits some. !
I'm afraid the flow chart might not quite be that simple. It is possible for there to be proposals that (1) don't appear in the Commit and (2) don't need to be re-sent. The simplest case is when a member sends two Update messages within an epoch. On the one hand, there's no practical need for them both to be included in the Commit, since one will overwrite the other. On the other hand, at the same time, the new member shouldn't re-send the stale Update. The idea of the array was to provide a slot for these to be included in the Commit, but without the need for recipients to act on them.
On a separate note, it would be helpful if you could base your different PRs on master, to make the specific changes clearer.
In that specific example, the member is aware of all the Updates they've sent and can know to only expect one to be included. In fact, the member can try to enforce a lot of things: Re-send if no Update was included. Re-send if the included Update isn't "recent enough." Re-send if the included Update isn't the most recent. The member's own sense of what should/shouldn't be included is what matters here, not the comitter's.
I can agree that the difference between "Proposal got dropped somewhere in the ether" and "Proposal was consciously ignored by the Committer" is not really salient. I'll post to the list to see if anyone else objects.
NAME raised some concerns with this on the mailing list. I proposed a compromise there, of the form: Specify that if a Committer receives two Updates proposals for the same leaf in the same epoch, then it MUST commit the latest one it received. State explicitly that the only Proposals that may be omitted from a Commit are: Invalid proposals (e.g., because of a bad signature, parsing error, reference to a non-existent leaf) Proposals whose effect on the tree is overwritten by another proposal, which at this point includes only: Updates for a leaf prior to the latest Update received by the committer Updates for a leaf for which there is a Remove in the epoch
NAME - How does that strike you?
Isn't that what's currently in the PR? The only difference is "SHOULD prefer the most recent Update" because it's not enforceable anyway
Here's the Weekly Digest for : - - Last week 5 issues were created. Of these, 1 issues have been closed and 4 issues are still open. :greenheart: , by :greenheart: , by :greenheart: , by :greenheart: , by :heart: , by :speaker: , by It received 3 comments. - - Last week, 8 pull requests were created, updated or merged. Last week, 7 pull requests were updated. :yellowheart: , by :yellowheart: , by :yellowheart: , by :yellowheart: , by :yellowheart: , by :yellowheart: , by :yellowheart: , by Last week, 1 pull request was merged. :purpleheart: , by - - Last week there were 3 commits. :hammerandwrench: by :hammerandwrench: by :hammerandwrench: by - - Last week there were 2 contributors. :bustinsilhouette: :bustinsilhouette: - - Last week there were no stargazers. - - Last week there were no releases. - - That's all for last week, please :eyes: Watch and :star: Star the repository to receive next weekly updates. :smiley: You can also .

representation of the state of the group by applying the proposals and advancing the key schedule.  Each proposal covered by the Commit is identified by a ProposalID value, which contains the hash of the MLSPlaintext in which the  <ins> Proposal was sent, using the hash function from the group's </ins> ciphersuite.  <ins> A group member that has observed one or more proposals within an epoch MUST send a Commit message before sending application data. This ensures, for example, that any members whose removal was proposed during the epoch are actually removed before any application data is transmitted. The sender of a Commit MUST include all valid proposals that it has received during the current epoch.  Invalid proposals include, for example, proposals with an invalid signature or proposals that are semantically invalid, such as an Add when the sender does not have the application-level permission to add new users.  If there are multiple proposals that apply to the same leaf, the committer chooses one and includes only that one in the Commit, considering the rest invalid.  The committer MUST prefer any Remove received, or the most recent Update for the leaf if there are no Removes.  If there are multiple Add proposals for the same client, the committer again chooses one to include and considers the rest invalid. The Commit MUST NOT combine proposals sent within different epochs. In the event that a valid proposal is omitted from the next Commit, the sender of the proposal SHOULD retransmit it in the new epoch. </ins> [[ OPEN ISSUE: This structure loses the welcome_info_hash, because new participants are no longer expected to have access to the Commit A member of the group creates a Commit message and the corresponding Welcome message at the same time, by taking the following steps:  <ins> Construct an initial Commit object with "updates", "removes", and "adds" fields populated from Proposals received during the current epoch, and an empty "path" field. </ins> Generate a provisional GroupContext object by applying the proposals referenced in the initial Commit object in the order Regardless of how messages are kept in sequence, implementations MUST only update their cryptographic state when valid Commit messages are  <ins> received.  Generation of Commit messages MUST NOT modify a client's state, since the endpoint doesn't know at that time whether the changes implied by the Commit message will succeed or not. </ins> 11.1. With this approach, the delivery service ensures that incoming messages are added to an ordered queue and outgoing messages are  <ins> dispatched in the same order.  The server is trusted to break ties when two members send a Commit message at the same time. </ins> Messages should have a counter field sent in clear-text that can be checked by the server and used for tie-breaking.  The counter starts


Construct an initial Commit object with "updates", "removes", and "adds" fields populated from Proposals received during the current <del> epoch, and an empty "path" field. </del>  Generate a provisional GroupContext object by applying the proposals referenced in the initial Commit object in the order The "commit_secret" is the value "path_secret[n+1]" derived from the "path_secret[n]" value associated to the root node.  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

You can now rebase this on master for clarity.
Here's the Weekly Digest for : - - Last week 5 issues were created. Of these, 1 issues have been closed and 4 issues are still open. :greenheart: , by :greenheart: , by :greenheart: , by :greenheart: , by :heart: , by :speaker: , by It received 3 comments. - - Last week, 8 pull requests were created, updated or merged. Last week, 7 pull requests were updated. :yellowheart: , by :yellowheart: , by :yellowheart: , by :yellowheart: , by :yellowheart: , by :yellowheart: , by :yellowheart: , by Last week, 1 pull request was merged. :purpleheart: , by - - Last week there were 3 commits. :hammerandwrench: by :hammerandwrench: by :hammerandwrench: by - - Last week there were 2 contributors. :bustinsilhouette: :bustinsilhouette: - - Last week there were no stargazers. - - Last week there were no releases. - - That's all for last week, please :eyes: Watch and :star: Star the repository to receive next weekly updates. :smiley: You can also .
This looks good, just one minor clarification

Construct an initial Commit object with "updates", "removes", and "adds" fields populated from Proposals received during the current  <ins> epoch, and empty "client_init_key" and "path" fields. </ins> Generate a provisional GroupContext object by applying the proposals referenced in the initial Commit object in the order The "commit_secret" is the value "path_secret[n+1]" derived from the "path_secret[n]" value associated to the root node. <ins> Generate a new ClientInitKey for the Committer's own leaf.  Store it in the ratchet tree and assign it to the "client_init_key" field in the Commit object. </ins> 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


While hashes at the nodes are used to check the integrity of the subtrees, signatures are required to provide authentication and group <del> agreement.  Siganatures are especially important in the case of </del>  newcomers and MUST be verified when joining.  All nodes in the tree MUST be signed to provide authentication and group agreement.

These are resolved in .
Here's the Weekly Digest for : - - Last week 5 issues were created. Of these, 1 issues have been closed and 4 issues are still open. :greenheart: , by :greenheart: , by :greenheart: , by :greenheart: , by :heart: , by :speaker: , by It received 3 comments. - - Last week, 8 pull requests were created, updated or merged. Last week, 7 pull requests were updated. :yellowheart: , by :yellowheart: , by :yellowheart: , by :yellowheart: , by :yellowheart: , by :yellowheart: , by :yellowheart: , by Last week, 1 pull request was merged. :purpleheart: , by - - Last week there were 3 commits. :hammerandwrench: by :hammerandwrench: by :hammerandwrench: by - - Last week there were 2 contributors. :bustinsilhouette: :bustinsilhouette: - - Last week there were no stargazers. - - Last week there were no releases. - - That's all for last week, please :eyes: Watch and :star: Star the repository to receive next weekly updates. :smiley: You can also .

While hashes at the nodes are used to check the integrity of the subtrees, signatures are required to provide authentication and group  <ins> agreement.  Signatures are especially important in the case of </ins> newcomers and MUST be verified when joining.  All nodes in the tree MUST be signed to provide authentication and group agreement.


The goal of the reinit proposal is to trigger a tear down / restart of a group with the same members but different parameters such as a version upgrade, or a cipher suite change. Changing version - version is a field within key package, and this locks a key package to a specific version, even if capabilities allow for multiple versions. Should that field be removed, and instead you just look at capabilities? It seems that this would change with since leaf nodes will not be pinned to a specific version, just a cipher suite. After 575 gets merged one could upgrade versions using a new tree that copies the current non-empty leaves so long as each leaf has the upgraded version in their capabilities set. That specific tree building process is not mentioned, it is just sort of assumed in context. Changing cipher suite - In the case you want to change cipher suite, complexity somewhat increases. You will need to create a new tree by grabbing new key packages for each participant, and because the cipher suite has changed, all of the credentials have also changed. It is mentioned that "A group may be reinitialized by creating a new group with the same membership and different parameters", but this does not have a statement that has a SHOULD or MUST associated with it. For example, in my 5000 member group 1 member does not support the cipher suite that we are migrating away from. As the committer, I'm assuming that I should keep everyone in the group as best effort, but have the option to filter out problematic members that are blocking a migration due to them being offline for long periods of time or having outdated software. Due to the credential change, enforcing a MUST might be difficult since equality between two x509 credentials is not straight forward for example. To summarize, I propose some changes Remove version from key package, and rely on capabilities instead, this may now be irrelevant due to , but the version and capabilities.versions values seem duplicative Add some language in reinitialization that clarifies the flow for upgrading just version vs changing cipher suite, where changing cipher suite explicitly requires you acquire new key packages for each member. Clarify that a person issuing the welcome based on a reinit proposal SHOULD include all existing members
IMO those fields have quite separate purposes. Keep in mind that a different version could have a different KeyPackage format. So says "this KeyPackage is formatted according to this version", while the capabilities says what other versions I support. With that in mind, you need to grab new KeyPackages for everyone regardless of whether you're changing version, ciphersuite, or both. If this isn't clear, we should certainly clarify it. If you're worried about people not supporting the new stuff, you could maintain the hard semantic just by Removing those participants that don't support the new stuff before you ReInit.
Fair enough that seems reasonableI don't think it's necessarily clear right now, it's more of an assumption based on an understanding of the protocol overall. I'll try to come up with something there to give more of a step by step explanation of the flow.If this is a hard requirement should the doc say MUST here? If so, I'm trying to think through what equality means in terms of membership when in the event the cipher suite changes, all the credentials will also change.
On (3) -- I would be OK with saying that the new members MUST be the same as the old members. The notion of "same" is a little squishy, given ambiguities around identifiers, but it seems like it will probably be clear in practice. W.r.t. removing people to make an upgrade possible, when the application decides to upgrade and how the application it meets the same-member criterion is up to the application.
Discussion on virtual interim: Maybe put "remove before upgrade" in implementation considerations document.
NAME I meant to make this call to discuss, seem to not have the invite anymore, can you forward it to me? In terms of where this ticket is at, I was mostly raising the concerns to clarify for myself that what I think is the process is actually the process. I am willing to just close this one out

5.1. <ins> Each endpoint caches the following state: For the group: The current epoch The list of identity keys for the group The current frontier The current preStageKey For the endpoint: Index in the tree Leaf key pair Copath </ins> 5.2. 5.2.1.


verify Sender.sender_type is "member" and use Sender.sender for MLSSenderData.sender.  The Additional Authenticated Data (AAD) for the SenderData ciphertext computation is its prefix in the MLSCiphertext, namely: sender) application key chain for the current epoch, according to the type of message being encrypted.  The Additional Authenticated Data (AAD) input to the encryption contains an object of the following form, with the values used to identify the key and nonce:

cc NAME
Quick question without reading much, but why can't we reuse the sender data nonce which is always fresh to do this?
You could do that; in fact you could just use that nonce for the content encryption as well as the sender data encryption, and forget about the key-schedule derived nonces as well. The reason I didn't go down that path is that I'm generally leery of requiring fresh randomness in the protocol. In particular, for the sender data, I think we should be moving toward masking instead of GCM (cf ). If we do masking, then we won't need to change this, and we'll have basically the minimum amount of fresh entropy needed.
Nope you can't really do that. The xoring of the two is strictly stronger, even if your PRNG is bad, you'll benefit from the entropy of the deterministic nonce because of the current epoch secret to which many people have contributed to. Until we do that move to masking, I think I would prefer to have the xor use the sender data nonce.
I'm confused about what you're talking about. In the current proposal: I agree that that's better than just using the for the reasons you say. But I thought what you were proposing was : Either way, we're incorporating the key schedule nonce. How about the following proposal? For now: If we do masking later, we do something like what's in this PR
Works for me !
NAME if the revised scheme looks ok to you, I'll consider this ready to merge
If I had a concern with the new scheme, it's that it's no longer resistant to nonce misuse. For example, consider somebody setting .
That would violate the requirement that the be fresh. I've clarified that requirement in the latest commit.
Putting a requirement in the spec is good. But that won't stop a nonce-misusing implementation from running, which was the point of deterministically generating the nonce in the first place.
I'm not sure why you think a freshness requirement is less plausible for the than for the . Neither is enforceable in the protocol. !
The value of is that it's only 4 bytes, so it doesn't mask the whole thing. Worst case, can reliably fix 32 bits, leaving only 64 bits of actual randomness in the nonce. Not good, but better than 0, and better than invoking a hash to actually mix things.
The fact that is not called a nonce and is a different size also makes me feel better about it not being misused
NAME NAME I don't have a strong opinion here, I was just pointing that we already has a fresh value. I can see why Brendan prefers the smaller guard using a fresh distinct value, so I'll support this or any variant for which you both agree ; )
Reverted to the approach, which it seems everyone is OK with.
Earlier, we had considered a "masking" approach for sender data, much like QUIC does for . This approach would save 28 bytes of overhead (nonce + tag), but was abandoned because of uncertainty around the security of the masking approach. Since then, Bellare et al. published , which includes what is effectively a proof of the security properties of the QUIC scheme. The QUIC scheme is essentially scheme HN1 of the paper, the main difference being that QUIC encrypt not the nonce directly, but a packet number which is used to compute the nonce. Our application would be similar, since we would encrypt metadata used to encrypt the key and the nonce. Given this new analysis, is it time to bring back masking?
Fixed by
Nonces and keys are generated deterministically. If a client's state is rolled back to a previous version (because the process crashed, for instance), then the client is very likely to re-use the same key+nonce pair to encrypt a different application message. This breaks the confidentiality of the AEAD. Consider using explicit nonces (even small ones will do) or SIV-like constructions.
Denis Jackson already discussed this, the WG, including I, was pretty conservative at the time but I am feeling more and more we should do it, indeed… There are other linked problems that I’ll discuss today but in general, in case of recoverable state loss I would strongly favor mandating an update which would definitely help with the problem. Detecting such losses is probably not trivial though :) B.
NAME Notes for reference:The other solution we thought about was using the fresh explicit nonce as a re-randomizing solution for the deterministic handshake nonce.
Approved.This is perfect! Thanks!

verify Sender.sender_type is "member" and use Sender.sender for MLSSenderData.sender. <ins> The "reuse_guard" field contains a fresh random value used to avoid nonce reuse in the case of state loss or corruption, as described in content-signing-and-encryption. </ins> The Additional Authenticated Data (AAD) for the SenderData ciphertext computation is its prefix in the MLSCiphertext, namely: sender) application key chain for the current epoch, according to the type of message being encrypted. <ins> Before use in the encryption operation, the nonce is XORed with a fresh random value to guard against reuse.  Because the key schedule generates nonces deterministically, a client must keep persistent state as to where in the key schedule it is; if this persistent state is lost or corrupted, a client might reuse a generation that has already been used, causing reuse of a key/nonce pair. To avoid this situation, the sender of a message MUST generate a fresh random 4-byte "reuse guard" value and XOR it with the first four bytes of the nonce from the key schedule before using the nonce for encryption.  The sender MUST include the reuse guard in the "reuse_guard" field of the sender data object, so that the recipient of the message can use it to compute the nonce to be used for decryption. </ins> The Additional Authenticated Data (AAD) input to the encryption contains an object of the following form, with the values used to identify the key and nonce:


is the concatenation of that node's parent along with the parent's direct path.  The _copath_ of a node is the node's sibling concatenated with the list of siblings of all the nodes in its direct <del> path.  The _frontier_ of a tree is the list of heads of the maximal full subtrees of the tree, ordered from left to right. </del>  For example, in the below tree: The copath of C is (D, AB, EFG) <del> The frontier of the tree is (ABCD, EF, G) </del> Each node in the tree is assigned a _node index_, starting at zero and running from left to right.  A node is a leaf node if and only if it has an even index.  The node indices for the nodes in the above generated value to provide post-compromise security. The generator of the Commit starts by using the HPKE secret key <del> "leaf_hpke_secret_key" associated with the new leaf ClientInitKey (see initialization-keys) to compute "path_secret[0]" and generate a </del>  sequence of "path secrets", one for each ancestor of its leaf.  That is, path_secret[0] is used for the node directly above the leaf, path_secret[1] for its parent, and so on.  At each step, the path For example, suppose there is a group with four members: If member B subsequently generates a Commit based on a secret <del> "leaf_hpke_secret_key", then it would generate the following sequence of path secrets and node secrets: </del>  After the Commit, the tree will have the following structure, where <del> "ns[i]" represents the node_secret values generated as described above: </del>  5.5. algorithm described above. The recipient SHOULD verify that the received public keys agree <del> with the public keys derived from the new node_secret values. </del>  Merge the updated path secrets into the tree. always has a zero-length hash for this value. For nodes where an updated path secret was computed in step 1, <del> compute the corresponding node secret and node key pair and replace the values stored at the node with the computed values. </del>  For example, in order to communicate the example update described in the previous section, the sender would transmit the following values: The signature algorithm specified in the ciphersuite is the mandatory algorithm to be used for the signatutes in MLSPlaintext and the tree signatures.  It can be different from the signature algorithm <del> specified in credential fielf of ClientInitKeys. </del>  The ciphersuites are defined in section mls-ciphersuites. <del> 6.1.1. </del> Depending on the Diffie-Hellman group of the ciphersuite, different rules apply to private key derivation and public key verification.  <del> 6.1.1.1. Given an octet string X, the private key produced by the Derive-Key- Pair operation is SHA-256(X) (Recall that any 32-octet string is a valid X25519 private key).  The corresponding public key is X25519(SHA-256(X), 9). Implementations SHOULD use the approach specified in RFC7748 to calculate the Diffie-Hellman shared secret.  Implementations MUST check whether the computed Diffie-Hellman shared secret is the all- zero value and abort if so, as described in Section 6 of RFC7748.  If implementers use an alternative implementation of these elliptic curves, they SHOULD perform the additional checks specified in Section 7 of </del>  <del> 6.1.1.2. </del>  <del> Given an octet string X, the private key produced by the Derive-Key- Pair operation is SHA-512(X) truncated to 448 bits (Recall that any 56-octet string is a valid X448 private key).  The corresponding public key is X448(SHA-512(X), 5). </del>  <del> Implementations SHOULD use the approach specified in RFC7748 to </del>  calculate the Diffie-Hellman shared secret.  Implementations MUST check whether the computed Diffie-Hellman shared secret is the all- zero value and abort if so, as described in Section 6 of RFC7748.  If implementers use an alternative implementation of these elliptic <del> curves, they SHOULD perform the additional checks specified in </del>  Section 7 of <del> 6.1.1.3. </del>  <del> Given an octet string X, the private key produced by the Derive-Key- Pair operation is SHA-256(X), interpreted as a big-endian integer. The corresponding public key is the result of multiplying the standard P-256 base point by this integer. </del>  <del> P-256 ECDH calculations (including parameter and key generation as well as the shared secret calculation) are performed according to IEEE1363 using the ECKAS-DH1 scheme with the identity map as key derivation function (KDF), so that the shared secret is the x-coordinate of the ECDH shared secret elliptic curve point </del>  represented as an octet string.  Note that this octet string (Z in IEEE 1363 terminology) as output by FE2OSP, the Field Element to Octet String Conversion Primitive, has constant length for any given

This deals cleanly with groups where the private key size doesn't align neatly with a hash function output size (e.g., X448), and saves a few hash invocations by removing intermediate values. A few editorial changes are included here as well. Depends on .
As discussed on call, folks are comfortable with this. Ready to merge, pending
Please merge when get a chance.

is the concatenation of that node's parent along with the parent's direct path.  The _copath_ of a node is the node's sibling concatenated with the list of siblings of all the nodes in its direct  <ins> path. </ins> For example, in the below tree: The copath of C is (D, AB, EFG)  Each node in the tree is assigned a _node index_, starting at zero and running from left to right.  A node is a leaf node if and only if it has an even index.  The node indices for the nodes in the above generated value to provide post-compromise security. The generator of the Commit starts by using the HPKE secret key  <ins> "leaf_hpke_secret" associated with the new leaf ClientInitKey (see initialization-keys) to compute "path_secret[0]" and generate a </ins> sequence of "path secrets", one for each ancestor of its leaf.  That is, path_secret[0] is used for the node directly above the leaf, path_secret[1] for its parent, and so on.  At each step, the path For example, suppose there is a group with four members: If member B subsequently generates a Commit based on a secret  <ins> "leaf_hpke_secret", then it would generate the following sequence of path secrets: </ins> After the Commit, the tree will have the following structure, where  <ins> "np[i]" represents the node_priv values generated as described above: </ins> 5.5. algorithm described above. The recipient SHOULD verify that the received public keys agree  <ins> with the public keys derived from the new path_secret values. </ins> Merge the updated path secrets into the tree. always has a zero-length hash for this value. For nodes where an updated path secret was computed in step 1,  <ins> compute the corresponding node key pair and replace the values stored at the node with the computed values. </ins> For example, in order to communicate the example update described in the previous section, the sender would transmit the following values: The signature algorithm specified in the ciphersuite is the mandatory algorithm to be used for the signatutes in MLSPlaintext and the tree signatures.  It can be different from the signature algorithm  <ins> specified in credential field of ClientInitKeys. </ins> The ciphersuites are defined in section mls-ciphersuites.  Depending on the Diffie-Hellman group of the ciphersuite, different rules apply to private key derivation and public key verification. <ins> For all ciphersuites defined in this document, the Derive-Key-Pair function begins by deriving a "key pair secret" of appropriate length, then converting it to a private key in the required group. The ciphersuite specifies the required length and the conversion. </ins>  <ins> 6.1.1. </ins>  <ins> For X25519, the key pair secret is 32 octets long.  No conversion is required, since any 32-octet string is a valid X25519 private key. The corresponding public key is X25519(SHA-256(X), 9). </ins>  <ins> For X448, the key pair secret is 56 octets long.  No conversion is required, since any 56-octet string is a valid X448 private key.  The corresponding public key is X448(SHA-256(X), 5). </ins>  <ins> Implementations MUST use the approach specified in RFC7748 to </ins> calculate the Diffie-Hellman shared secret.  Implementations MUST check whether the computed Diffie-Hellman shared secret is the all- zero value and abort if so, as described in Section 6 of RFC7748.  If implementers use an alternative implementation of these elliptic  <ins> curves, they MUST perform the additional checks specified in </ins> Section 7 of  <ins> 6.1.1.1. </ins>  <ins> For P-256, the key pair secret is 32 octets long.  For P-521, the key pair secret is 66 octets long.  In either case, the private key derived from a key pair secret is computed by interpreting the key pair secret as a big-endian integer. </ins>  <ins> ECDH calculations for these curves (including parameter and key generation as well as the shared secret calculation) are performed according to IEEE1363 using the ECKAS-DH1 scheme with the identity map as key derivation function (KDF), so that the shared secret is the x-coordinate of the ECDH shared secret elliptic curve point </ins> represented as an octet string.  Note that this octet string (Z in IEEE 1363 terminology) as output by FE2OSP, the Field Element to Octet String Conversion Primitive, has constant length for any given


Identify the lowest common ancestor in the tree of the new member's leaf node and the member sending the Commit <del> Compute the path secret corresponding to the commonn ancestor </del>  node Compute an EncryptedKeyPackage object that encapsulates the For each parent of the common ancestor, up to the root of the tree, derive a new path secret and set the private key for the node to the private key derived from the path secret.  The <del> private key MUST be the private key that correspondns to the </del>  public key in the node. Use the "epoch_secret" from the KeyPackage object to generate the signatures on the messages under identity keys. [[ OPEN ISSUE: Signatures under the identity keys, while simple, have <del> the side-effect of preclude deniability.  We may wish to allow other options, such as (ii) a key chained off of the identity key, or (iii) some other key obtained through a different manner, such as a </del>  pairwise channel that provides deniability for the message contents.]]

This PR aims to fix some odd formulations, whilst maintaining the intended meaning of the sentences.
Thank you!

Identify the lowest common ancestor in the tree of the new member's leaf node and the member sending the Commit  <ins> Compute the path secret corresponding to the common ancestor </ins> node Compute an EncryptedKeyPackage object that encapsulates the For each parent of the common ancestor, up to the root of the tree, derive a new path secret and set the private key for the node to the private key derived from the path secret.  The  <ins> private key MUST be the private key that corresponds to the </ins> public key in the node. Use the "epoch_secret" from the KeyPackage object to generate the signatures on the messages under identity keys. [[ OPEN ISSUE: Signatures under the identity keys, while simple, have  <ins> the side-effect of precluding deniability.  We may wish to allow other options, such as (ii) a key chained off of the identity key, or (iii) some other key obtained through a different manner, such as a </ins> pairwise channel that provides deniability for the message contents.]]


An agent that uses this protocol to establish shared cryptographic state with other clients.  A client is defined by the <del> cryptographic keys it holds.  An application or user may use one client per device (keeping keys local to each device) or sync keys among a user's devices so that each user appears as a single client. </del>  A collection of clients with shared cryptographic state.

Could you please just restore the PSK part of the KeySchedule, I’ll handle that separately. Ps: we wouldn’t have forgot you... :) we intended to go back to the list later, but I’ll merge it now...

An agent that uses this protocol to establish shared cryptographic state with other clients.  A client is defined by the  <ins> cryptographic keys it holds. </ins> A collection of clients with shared cryptographic state.


specification for more information). The signature algorithm specified in the ciphersuite is the mandatory <del> algorithm to be used for the signatutes in MLSPlaintext and the tree signatures.  It can be different from the signature algorithm specified in credential field of ClientInitKeys. </del>  The ciphersuites are defined in section mls-ciphersuites.

required a common signature algorithm, but allowed credentials to deviate. This PR adds the requirement that credentials be consistent.

specification for more information). The signature algorithm specified in the ciphersuite is the mandatory  <ins> algorithm to be used for signatures in MLSPlaintext and the tree signatures.  It MUST be the same as the signature algorithm specified in the credential field of the ClientInitKey objects in the leaves of the tree (including the ClientInitKeys used to add new members). </ins> The ciphersuites are defined in section mls-ciphersuites.


RFC EDITOR PLEASE DELETE THIS SECTION.  draft-08 Change ClientInitKeys so that they only refer to one ciphersuite This specification defines the following ciphersuites for use with MLS 1.0. <del> The KEM/DEM constructions used for HPKE are defined by HPKE.  The corresponding AEAD algorithms AEAD_AES_128_GCM and AEAD_AES_256_GCM, are defined in RFC5116.  AEAD_CHACHA20_POLY1305 is defined in RFC7539.  The corresponding hash algorithms are defined in SHS. </del>  It is advisable to keep the number of ciphersuites low to increase the chances clients can interoperate in a federated environment,

Since draft-08, we have neglected to update the changelog, despite a bunch of significant changes. Also, because our CI is busted, some build failures crept in.

RFC EDITOR PLEASE DELETE THIS SECTION. <ins> draft-09 Remove blanking of nodes on Add (*) Change epoch numbers to uint64 (*) Add PSK inputs (*) Add key schedule exporter (*) Sign the updated direct path on Commit, using "parent hashes" and one signature per leaf (*) Use structured types for external senders (*) Redesign Welcome to include confirmation and use derived keys (*) Remove ignored proposals (*) Always include an Update with a Commit (*) Add per-message entropy to guard against nonce reuse (*) Use the same hash ratchet construct for both application and handshake keys (*) Add more ciphersuites Use HKDF to derive key pairs (*) Mandate expiration of ClientInitKeys (*) Add extensions to GroupContext and flesh out the extensibility story (*) </ins> draft-08 Change ClientInitKeys so that they only refer to one ciphersuite This specification defines the following ciphersuites for use with MLS 1.0.  <ins> The KEM/DEM constructions used for HPKE are defined by I-D.irtf-cfrg- hpke.  The corresponding AEAD algorithms AEAD_AES_128_GCM and AEAD_AES_256_GCM, are defined in RFC5116.  AEAD_CHACHA20_POLY1305 is defined in RFC7539.  The corresponding hash algorithms are defined in SHS. </ins> It is advisable to keep the number of ciphersuites low to increase the chances clients can interoperate in a federated environment,


RFC EDITOR PLEASE DELETE THIS SECTION.  draft-09 Remove blanking of nodes on Add (*) In the event that a valid proposal is omitted from the next Commit, the sender of the proposal SHOULD retransmit it in the new epoch. <del> [[ OPEN ISSUE: This structure loses the welcome_info_hash, because new participants are no longer expected to have access to the Commit message adding them to the group.  It might be we need to re- introduce this assumption, though it seems like the information confirmed by the welcome_info_hash is confirmed at the next epoch change anyway. ]] </del>  A member of the group creates a Commit message and the corresponding Welcome message at the same time, by taking the following steps: unoccupied leaf, or appended to the right edge of the tree if all leaves are occupied. <del> Create a DirectPath using the new tree (which includes any new members).  The GroupContext for this operation uses the "group_id", "epoch", "tree_hash", and "confirmed_transcript_hash" values in the initial GroupContext object. </del>  <del> Assign this DirectPath to the "path" fields in the Commit. </del>  Apply the DirectPath to the tree, as described in synchronizing-views-of-the-tree.  Define "commit_secret" as the value "path_secret[n+1]" derived from the "path_secret[n]" value assigned to the root node.  Generate a new KeyPackage for the Committer's own leaf, with a "parent_hash" extension.  Store it in the ratchet tree and assign it to the "key_package" field in the Commit object. Identify the lowest common ancestor in the tree of the new member's leaf node and the member sending the Commit <del> Compute the path secret corresponding to the common ancestor node </del>  Compute an EncryptedGroupSecrets object that encapsulates the <del> "init_secret" for the current epoch and the path secret for the common ancestor. </del>  Construct a Welcome message from the encrypted GroupInfo object and the encrypted group secrets. leaf, or appended to the right edge of the tree if all leaves are occupied. <del> Process the "path" value using the ratchet tree the provisional GroupContext, to update the ratchet tree and generate the "commit_secret": </del>  Apply the DirectPath to the tree, as described in synchronizing-views-of-the-tree, and store "key_package" at the Define "commit_secret" as the value "path_secret[n+1]" derived from the "path_secret[n]" value assigned to the root node. <del> Update the new GroupContexts confirmed and interim transcript </del>  hashes using the new Commit. Use the "commit_secret", the provisional GroupContext, and the Update the leaf at index "index" with the private key corresponding to the public key in the node.  Identify the lowest common ancestor of the leaves at "index" and at "GroupInfo.signer_index".  Set the private key for this <del> node to the private key derived from the "path_secret" in the GroupSecrets object. </del>  For each parent of the common ancestor, up to the root of the tree, derive a new path secret and set the private key for the

When we converted to the Proposal/Commit scheme, one of the consequences was that Adds became O(N) instead of O(1), since they had to Committed and commits encrypt the committer's direct path to the remainder of the tree. This is ~log(N) in the nice case and ~N in the worst case. At the time, we noted that one could special-case Add-only Commits in the future and make the update to the direct path optional in that case. This PR implements that suggestion. The leaf KeyPackage for the Commit is moved into the DirectPath struct, and the whole DirectPath is made optional. The optional value MUST be provided for: A Commit committing an update or remove A "just update my path" Commit covering no proposals
So we do go forward one epoch, but with an all-zero commit secret. Do I understand that correctly?
NAME That's correct. The idea is that you don't need to send the current group any additional entropy, you just need them to hash forward. Note that this could change the FS/PCS guarantees available. But I don't think we ever proposed that Add would cause any FS/PCS changes to the group, so maybe this doesn't matter.
Add should give FS guarantees in so far as that the new member shouldn't be able to decrypt old messages or derive old keys. But I think ratcheting forward should do the trick. We should note, though, that this breaks the (implicit) assumption that a commit always introduces fresh key material to the group. As far as I can see, that's not breaking anything right now, but we should keep it in mind.
Note: The latest commit fixes a gap that was discovered in .
See the flexibility question.

RFC EDITOR PLEASE DELETE THIS SECTION. <ins> draft-10 Re-enable constant-time Add (*) </ins> draft-09 Remove blanking of nodes on Add (*) In the event that a valid proposal is omitted from the next Commit, the sender of the proposal SHOULD retransmit it in the new epoch.  <ins> The "path" field of a Commit message MUST be populated if the Commit covers at least one Update or Remove proposal, i.e., if the length of the "updates" or "removes vectors is greater than zero.  The "path"field MUST also be populated if the Commit covers no proposals at all (i.e., if all three proposal vectors are empty).  The "path"field MAY be omitted if the Commit covers only Add proposals. In pseudocode, the logic for whether the "path` field is required is as follows: </ins> A member of the group creates a Commit message and the corresponding Welcome message at the same time, by taking the following steps: unoccupied leaf, or appended to the right edge of the tree if all leaves are occupied.  <ins> Decide whether to populate the "path" field: If the "path" field is required based on the proposals that are in the commit (see above), then it MUST be populated.  Otherwise, the sender MAY omit the "path" field at its discretion. If populating the "path" field: Create a DirectPath using the new tree (which includes any new members).  The GroupContext for this operation uses the "group_id", "epoch", "tree_hash", and "confirmed_transcript_hash" values in the initial GroupContext object. </ins>  <ins> Assign this DirectPath to the "path" field in the Commit. </ins> Apply the DirectPath to the tree, as described in synchronizing-views-of-the-tree.  Define "commit_secret" as the value "path_secret[n+1]" derived from the "path_secret[n]" value assigned to the root node. <ins> If not populating the "path" field: Set the "path" field in the Commit to the null optional.  Define "commit_secret" as the all- zero vector of the same length as a "path_secret" value would be. </ins> Generate a new KeyPackage for the Committer's own leaf, with a "parent_hash" extension.  Store it in the ratchet tree and assign it to the "key_package" field in the Commit object. Identify the lowest common ancestor in the tree of the new member's leaf node and the member sending the Commit  <ins> If the "path" field was populated above: Compute the path secret corresponding to the common ancestor node </ins> Compute an EncryptedGroupSecrets object that encapsulates the  <ins> "init_secret" for the current epoch and the path secret (if present). </ins> Construct a Welcome message from the encrypted GroupInfo object and the encrypted group secrets. leaf, or appended to the right edge of the tree if all leaves are occupied.  <ins> Verify that the "path" value is populated if either of the "updates" or "removes" vectors has length greater than zero, or if all of the "updates", "removes", and "adds" vectors are empty. Otherwise, the "path" value MAY be omitted. If the "path" value is populated: Process the "path" value using the ratchet tree the provisional GroupContext, to update the ratchet tree and generate the "commit_secret": </ins> Apply the DirectPath to the tree, as described in synchronizing-views-of-the-tree, and store "key_package" at the Define "commit_secret" as the value "path_secret[n+1]" derived from the "path_secret[n]" value assigned to the root node.  <ins> If the "path" value is not populated: Define "commit_secret" as the all-zero vector of the same length as a "path_secret" value would be. Update the new GroupContext's confirmed and interim transcript </ins> hashes using the new Commit. Use the "commit_secret", the provisional GroupContext, and the Update the leaf at index "index" with the private key corresponding to the public key in the node. <ins> If the "path_secret" value is set in the GroupSecrets object: </ins> Identify the lowest common ancestor of the leaves at "index" and at "GroupInfo.signer_index".  Set the private key for this  <ins> node to the private key derived from the "path_secret". </ins> For each parent of the common ancestor, up to the root of the tree, derive a new path secret and set the private key for the


Generate a provisional GroupContext object by applying the proposals referenced in the initial Commit object in the order <del> 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. </del>  Create a DirectPath using the new tree (which includes any new members).  The GroupContext for this operation uses the Generate a provisional GroupContext object by applying the proposals referenced in the commit object in the order provided, <del> 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. </del>  Process the "path" value using the ratchet tree the provisional GroupContext, to update the ratchet tree and generate the

by updating the description for creating a commit to explicitly mention that first all update proposals are applied, then remove proposals, and last add proposals.
The writes: However, from this it is not immediately clear to me in which order the collections of proposals , , and are to be applied. I assume it should be in that order because it makes practical sense and because they're written in that order in the Commit struct. If my guess is correct, perhaps it would be useful to make this explicit? E.g.:
NAME - I agree with your interpretation here. Could you make a PR with some proposed wording?
Apologies for closing/opening the issue. I will open a PR to update the wording.

Generate a provisional GroupContext object by applying the proposals referenced in the initial Commit object in the order  <ins> provided, as described in proposals.  First the list of update proposals, then the list of remove proposals, and last the list of add 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. </ins> Create a DirectPath using the new tree (which includes any new members).  The GroupContext for this operation uses the Generate a provisional GroupContext object by applying the proposals referenced in the commit object in the order provided,  <ins> as described in proposals.  First the list of update proposals, then the list of remove proposals, and last the list of add 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. </ins> Process the "path" value using the ratchet tree the provisional GroupContext, to update the ratchet tree and generate the


GroupContext for the current epoch, so that signatures are specific to a given group and epoch.  The ciphertext field of the MLSCiphertext object is produced by supplying the inputs described below to the AEAD function specified by the ciphersuite in use.  The plaintext input contains content and might propose to remove a member of a group 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 <del> group are identified by an "preconfigured" or "new_member" SenderType </del>  in MLSPlaintext. The "new_member" SenderType is used for clients proposing that they

by special-casing external senders. Removing the GroupContext would allow me to take messages that user A sent in one instance of a group, and make it look like they're speaking in another group by re-encrypting the ciphertext.
Right now, the signature on an MLSPlaintext includes the GroupContext for the group. If we are going to allow Add proposals signed by the new member, then this will need to change, because the new member doesn't know the GroupContext. My preference would be to simply remove the GroupContext from the plaintext. The Group ID is already included, so copy/paste protection can be added that way. The other elements of the GroupContext effectively overlap with the Epoch that is already signed.
Haven't thought about this too deeply, but having the full GroupContext in MLSPlaintextTBS means that we know all the members agree on the state of the ratchet tree, the transcript, the extensions. Removing it means we would no longer have agreement on those, right?
We won't have confirmation of agreement as a result of signature verification. The question is whether that is necessary. If the MLSPlaintext was decrypted from an MLSCiphertext, for example, then the successful decryption of the MLSCiphertext implies agreement on all those things. I suppose you could also switch the signature input based on SenderType, but that seems messier.
I'm not so sure that decrypting an MLSCiphertext implies agreement, because the crypto is symmetric. A man-in-the-middle can re-encrypt but not re-sign.

GroupContext for the current epoch, so that signatures are specific to a given group and epoch. <ins> [[OPEN ISSUE: group_id and epoch are duplicated in the TBS and in GroupContext.  Think about how to de-duplicate.]] </ins> The ciphertext field of the MLSCiphertext object is produced by supplying the inputs described below to the AEAD function specified by the ciphersuite in use.  The plaintext input contains content and might propose to remove a member of a group 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  <ins> group are identified by a "preconfigured" or "new_member" SenderType </ins> in MLSPlaintext. The "new_member" SenderType is used for clients proposing that they


for nodes in its direct path that are being reset by the corresponding Commit. <del> In the description of the tree as a list of nodes, the "key_package" field for a node MUST be populated if and only if that node is a leaf in the tree. </del>  On receiving a Welcome message, a client processes it using the following steps: Verify the integrity of the ratchet tree.  For each non-empty parent node, verify that exactly one of the node's children are non-empty and have the hash of this node set as their "parent_hash" value (if the child is another confirmation key and the "confirmed_transcript_hash" from the GroupInfo.  11. This protocol includes a mechanism for negotiating extension the value of the extension in the GroupContext, according to the semantics of the specific extension. <del> A client joining a group MUST populate the GroupContext extensions with exactly the contents of the extensions field in the Welcome message.  If any extension is unrecognized (i.e., not contained in the corresponding KeyPackage), then the client MUST reject the Welcome message and not join the group. </del>  Note that the latter two requirements mean that all MLS extensions are mandatory, in the sense that an extension in use by the group

A couple of shortcomings to note on this PR: The joiner can't compute their private keys because they're not told where they are in the tree or how big the tree is. The former should be added to and the latter to . This approach assumes that the joiner gets the tree OOB before processing the Welcome, otherwise they can't verify the signature on the Welcome. (Since Welcome presumes they get the credential for the signer from the tree.) We could avoid the latter problem by including a credential for the signer and a "membership proof" comprising the hashes needed to tie that leaf to the tree hash in the Welcome. That could allow participants to remain "passive" (without a full tree, just their private keys) for longer, especially if such a proof could be provided on Commit as well. This might be a good candidate for an extension, though.
Discussion at the 2020-07-14 interim indicated that we should go ahead with this strategy, and just note the requirement that you have to get the tree before you process the Welcome.
I remember we discussed this in the past, but I don't think we ever spec'ed this out fully: Right now the whole tree is part of (which in turn is part of ). This can grow quite big in large groups (~17MB with 100k members with the MTI). The practical limitations are: The TLS representation of the tree is hard to parse efficiently (no skipping and no a priori information of how many nodes are in the tree) Out-of-band transmission (like server assist) is not possible Large messages like that are hard to handle on mobile devices, when they have to be downloaded in the background We should introduce an indirection, like a commit hash of the tree.

for nodes in its direct path that are being reset by the corresponding Commit.  <ins> The client processing a Welcome message will need to have a copy of the group's ratchet tree.  The tree can be provided in the Welcome message, in an extension of type "ratchet_tree".  If it is sent otherwise (e.g., provided by a caching service on the Delivery Service), then the client MUST download the tree before processing the Welcome. </ins> On receiving a Welcome message, a client processes it using the following steps: Verify the integrity of the ratchet tree. <ins> Verify that the tree hash of the ratchet tree matches the "tree_hash" field in the GroupInfo. </ins> For each non-empty parent node, verify that exactly one of the node's children are non-empty and have the hash of this node set as their "parent_hash" value (if the child is another confirmation key and the "confirmed_transcript_hash" from the GroupInfo. <ins> 10.3. By default, a GroupInfo message only provides the joiner with a commitment to the group's ratchet tree.  In order to process or generate handshake messages, the joiner will need to get a copy of the ratchet tree from some other source.  (For example, the DS might provide a cached copy.)  The inclusion of the tree hash in the GroupInfo message means that the source of the ratchet tree need not be trusted to maintain the integrity of tree. In cases where the application does not wish to provide such an external source, the whole public state of the ratchet tree can be provided in an extension of type "ratchet_tree", containing a "ratchet_tree" object of the following form: The presence of a "ratchet_tree" extension in a GroupInfo message does not result in any changes to the GroupContext extensions for the group.  The ratchet tree provided is simply stored by the client and used for MLS operations. If this extension is not provided in a Welcome message, then the client will need to fetch the ratchet tree over some other channel before it can generate or process Commit messages.  Applications should ensure that this out-of-band channel is provided with security protections equivalent to the protections that are afforded to Proposal and Commit messages.  For example, an application that encrypts Proposal and Commit messages might distribute ratchet trees encrypted using a key exchanged over the MLS channel. </ins> 11. This protocol includes a mechanism for negotiating extension the value of the extension in the GroupContext, according to the semantics of the specific extension.  <ins> If any extension in a GroupInfo message is unrecognized (i.e., not contained in the corresponding KeyPackage), then the client MUST reject the Welcome message and not join the group. The extensions populated into a GroupContext object are drawn from those in the GroupInfo object, according to the definitions of those extensions. </ins> Note that the latter two requirements mean that all MLS extensions are mandatory, in the sense that an extension in use by the group


Credentials MAY also include information that allows a relying party to verify the identity / signing key binding. <del> The SignatureScheme type represents a signature algorithm.  Signature public keys are opaque values in a format defined by the signature scheme. </del>  Note that each new credential that has not already been validated by the application MUST be validated against the Authentication Service. 15. This document requests the creation of the following new IANA <del> registries: MLS Ciphersuites (mls-ciphersuites).  All of these registries should be under a heading of "Messaging Layer Security", and assignments are made via the Specification Required policy RFC8126.  See de for additional information about the MLS Designated Experts (DEs). </del>  15.1. Ciphersuite names follow the naming convention: <del> Where VALUE is represented as two 8bit octets: </del>  <del> This specification defines the following ciphersuites for use with MLS 1.0. </del>  <del> The KEM/DEM constructions used for HPKE are defined by I-D.irtf-cfrg- hpke.  The corresponding AEAD algorithms AEAD_AES_128_GCM and AEAD_AES_256_GCM, are defined in RFC5116.  AEAD_CHACHA20_POLY1305 is defined in RFC7539.  The corresponding hash algorithms are defined in SHS. </del>  It is advisable to keep the number of ciphersuites low to increase the chances clients can interoperate in a federated environment, 15.2. <del> [[ OPEN ISSUE: pick DE mailing address.  Maybe mls-des@ or mls-de- pool. ]] </del>  Specification Required RFC8126 registry requests are registered after <del> a three-week review period on the MLS DEs' mailing list: TBD@ietf.org [1], on the advice of one or more of the MLS DEs.  However, to allow for the allocation of values prior to publication, the MLS DEs may approve registration once they are satisfied that such a specification will be published. </del>  Registration requests sent to the MLS DEs mailing list for review SHOULD use an appropriate subject (e.g., "Request to register value 16.1.  URIs <del> [1] mailto:TBD@ietf.org </del>  [2] mailto:iesg@ietf.org

This started off just fixing , then grew a bit. Defines an IANA registry for ExtensionType values Defines an IANA registry for CredentialType values Updates the Ciphersuites registry to share formatting with the other two, and to be more explicit about its ties to HPKE algorithms Removes the unnecessary field from ... and as a result removes the definition of (which is used nowhere else)
Now that we have a more robust extension mechanism, we need to have an IANA registry to manage extensions, including: Registry structure Registry policy and (probably) designated experts Reserved vendor space

Credentials MAY also include information that allows a relying party to verify the identity / signing key binding.  <ins> A BasicCredential is a raw, unauthenticated assertion of an identity/ key binding.  The format of the key in the "public_key" field is defined by the relevant ciphersuite: the group ciphersuite for a credential in a ratchet tree, the KeyPackage ciphersuite for a credential in a KeyPackage object. For ciphersuites using Ed25519 or Ed448 signature schemes, the public key is in the format specified RFC8032.  For ciphersuites using ECDSA with the NIST curves P-256 or P-521, the public key is the output of the uncompressed Elliptic-Curve-Point-to-Octet-String conversion according to SECG. </ins> Note that each new credential that has not already been validated by the application MUST be validated against the Authentication Service. 15. This document requests the creation of the following new IANA  <ins> registries: MLS Ciphersuites (mls-ciphersuites) MLS Extension Types (mls-extension-types) MLS Credential Types (mls-credential-types) All of these registries should be under a heading of "Messaging Layer Security", and assignments are made via the Specification Required policy RFC8126.  See de for additional information about the MLS Designated Experts (DEs). [[ RFC EDITOR: Please replace XXXX throughout with the RFC number assigned to this document ]] </ins> 15.1. Ciphersuite names follow the naming convention:  <ins> Where VALUE is represented as a sixteen-bit integer: The columns in the registry are as follows: Value: The numeric value of the ciphersuite Name: The name of the ciphersuite </ins>  <ins> Recommended: Whether support for this extension is recommended by the IETF MLS WG.  Valid values are "Y" and "N".  The "Recommended" column is assigned a value of "N" unless explicitly requested, and adding a value with a "Recommended" value of "Y" requires Standards Action RFC8126.  IESG Approval is REQUIRED for a Y->N transition. </ins>  <ins> Reference: The document where this ciphersuite is defined Initial contents: These ciphersuites map to HPKE primitives and TLS signature schemes as follows I-D.irtf-cfrg-hpke RFC8446: The hash used for HKDF computations in MLS is the same hash used in the HPKE KDF for the ciphersuite. </ins> It is advisable to keep the number of ciphersuites low to increase the chances clients can interoperate in a federated environment, 15.2.  <ins> This registry lists identifiers for extensions to the MLS protocol. The extension type field is two bytes wide, so valid extension type values are in the range 0x0000 to 0xffff. Template: Value: The numeric value of the extension type Name: The name of the extension type Message(s): The messages in which the extension may appear, drawn from the following list: KP: KeyPackage messages W: Welcome messages Recommended: Whether support for this extension is recommended by the IETF MLS WG.  Valid values are "Y" and "N".  The "Recommended" column is assigned a value of "N" unless explicitly requested, and adding a value with a "Recommended" value of "Y" requires Standards Action RFC8126.  IESG Approval is REQUIRED for a Y->N transition. Reference: The document where this extension is defined Initial contents: 15.3. This registry lists identifiers for types of credentials that can be used for authentication in the MLS protocol.  The extension type field is two bytes wide, so valid extension type values are in the range 0x0000 to 0xffff. Template: Value: The numeric value of the credential type Name: The name of the credential type Recommended: Whether support for this extension is recommended by the IETF MLS WG.  Valid values are "Y" and "N".  The "Recommended" column is assigned a value of "N" unless explicitly requested, and adding a value with a "Recommended" value of "Y" requires Standards Action RFC8126.  IESG Approval is REQUIRED for a Y->N transition. Reference: The document where this extension is defined Initial contents: 15.4. </ins> Specification Required RFC8126 registry requests are registered after  <ins> a three-week review period on the MLS DEs' mailing list: mls-reg- review@ietf.org [1], on the advice of one or more of the MLS DEs. However, to allow for the allocation of values prior to publication, the MLS DEs may approve registration once they are satisfied that such a specification will be published. </ins> Registration requests sent to the MLS DEs mailing list for review SHOULD use an appropriate subject (e.g., "Request to register value 16.1.  URIs  <ins> [1] mailto:mls-reg-review@ietf.org </ins> [2] mailto:iesg@ietf.org


In the event that a valid proposal is omitted from the next Commit, the sender of the proposal SHOULD retransmit it in the new epoch.  The "path" field of a Commit message MUST be populated if the Commit covers at least one Update or Remove proposal, i.e., if the length of the "updates" or "removes vectors is greater than zero.  The In pseudocode, the logic for whether the "path` field is required is as follows:  A member of the group creates a Commit message and the corresponding Welcome message at the same time, by taking the following steps:

I think we need to clearly define "FS / PCS with regard to", but once we do that, it seems like a fine phrase. That seems like a good topic for a review / update of the Security Considerations once we have the protocol mostly in shape, for which I have filed .
The not-protocol parts of the document, including security considerations and some of the operational considerations, are mostly untouched since the beginning of this work. As we get the protocol firmed up, we should revisit these parts to make sure they're up to date and give an accurate reflection of the concerns around the protocol.
One particular thing related to this that I noticed is that in the the first sentence: is outdated as (ii) can now also be an all-zero vector, namely if is not required and not set (as introduced in if I'm not mistaken).
I did a review of the Security Considerations and didn't see anything that needs fixing before WGLC. The point that NAME raises appears to have been fixed in some other PR.
Right now, it is possible to send a Commit that covers no proposals. This has the result that the leaf of the sender and its direct path are refreshed, but no other change to the tree is made. (The epoch also advances, of course.) This can be is a useful operation. For example, an Add wipes out the new joiner's direct path, so having the new joiner send a Commit over no proposals would fill in that direct path and make subsequent operations more efficient. We should clarify in some prose that (a) this is possible and (b) why it might be useful.
That works fine. I am a bit bothered by the phrasing regarding properties « with regard to ...». If you can find an alternate phrasing that would be

In the event that a valid proposal is omitted from the next Commit, the sender of the proposal SHOULD retransmit it in the new epoch. <ins> A member of the group MAY send a Commit that references no proposals at all, which would thus have empty vectors for "updates", "removes", and "adds".  Such a Commit resets the sender's leaf and the nodes along its direct path, and provides forward secrecy and post- compromise security with regard to the sender of the Commit.  An Update proposal can be regarded as a "lazy" version of this operation, where only the leaf changes and intermediate nodes are blanked out. </ins> The "path" field of a Commit message MUST be populated if the Commit covers at least one Update or Remove proposal, i.e., if the length of the "updates" or "removes vectors is greater than zero.  The In pseudocode, the logic for whether the "path` field is required is as follows: <ins> To summarize, a Commit can have three different configurations, with different uses: An "empty" Commit that references no proposals, which updates the committer's contribution to the group and provides PCS with regard to the committer. An "add-only" Commit that references only Add proposals, in which the path is optional.  Such a commit provides PCS with regard to the committer only if the path field is present. A "full" Commit that references proposals of any type, which provides FS with regard to any removed members and PCS for the committer and any updated members. </ins> A member of the group creates a Commit message and the corresponding Welcome message at the same time, by taking the following steps:


All trees used in this protocol are left-balanced binary trees.  A binary tree is _full_ (and _balanced_) if its size is a power of two and for any parent node in the tree, its left and right subtrees have <del> the same size.  If a subtree is full and it is not a subset of any other full subtree, then it is _maximal_. </del>  A binary tree is _left-balanced_ if for every parent, either the parent is balanced, or the left subtree of that parent is the largest full subtree that could be constructed from the leaves present in the parent's own subtree.  Given a list of "n" items, there is a unique left-balanced binary tree structure with these elements as leaves. <del> In such a left-balanced tree, the "k-th" leaf node refers to the "k-th" leaf node in the tree when counting from the left, starting from 0. </del> (Note that left-balanced binary trees are the same structure that is used for the Merkle trees in the Certificate Transparency protocol I- The leaves of the tree are indexed separately, using a _leaf index_, since the protocol messages only need to refer to leaves in the tree. <del> Like nodes, leaves are numbered left to right.  Note that given the above numbering, a node is a leaf node if and only if it has an even node index, and a leaf node's leaf index is half its node index.  The leaf indices in the above tree are as follows: </del>  0 = A the signature computation comprises all of the fields except for the signature field. <del> KeyPackage objects MUST contain at least two extensions, one of type "supported_versions" and one of type "supported_ciphersuites".  These extensions allow MLS session establishment to be safe from downgrade attacks on these two parameters (as discussed in group-creation), while still only advertising one version / ciphersuite per KeyPackage. </del>  As the "KeyPackage" is a structure which is stored in the Ratchet Tree and updated depending on the evolution of this tree, each time a Commit message is sent and processed.  These states are referred to as "epochs" and are uniquely identified among states of the group by eight-octet epoch values.  When a new group is <del> initialized, its initial state epoch 0x0000000000000000.  Each time a state transition occurs, the epoch number is incremented by one. </del>  [[ OPEN ISSUE: It would be better to have non-linear epochs, in order to tolerate forks in the history.  There is a need to discuss whether

I've put a bunch of little fixes in here. If you disagree with any of these changes, I can back them out or change them. maximal is defined but never used k-th leaf/leaf index is defined in two places; merge definitions change KeyPackage struct definition to reflect that ~2~ 3 extensions are required is also a required extension grammar fix add missing semicolon
One minor thing, otherwise LGTM

All trees used in this protocol are left-balanced binary trees.  A binary tree is _full_ (and _balanced_) if its size is a power of two and for any parent node in the tree, its left and right subtrees have  <ins> the same size. </ins> A binary tree is _left-balanced_ if for every parent, either the parent is balanced, or the left subtree of that parent is the largest full subtree that could be constructed from the leaves present in the parent's own subtree.  Given a list of "n" items, there is a unique left-balanced binary tree structure with these elements as leaves.  (Note that left-balanced binary trees are the same structure that is used for the Merkle trees in the Certificate Transparency protocol I- The leaves of the tree are indexed separately, using a _leaf index_, since the protocol messages only need to refer to leaves in the tree.  <ins> Like nodes, leaves are numbered left to right.  The node with leaf index "k" is also called the "k-th" leaf.  Note that given the above numbering, a node is a leaf node if and only if it has an even node index, and a leaf node's leaf index is half its node index.  The leaf indices in the above tree are as follows: </ins> 0 = A the signature computation comprises all of the fields except for the signature field.  <ins> KeyPackage objects MUST contain at least three extensions, one of type "supported_versions", one of type "supported_ciphersuites", and one of type "lifetime".  The "supported_versions" and "supported_ciphersuites" extensions allow MLS session establishment to be safe from downgrade attacks on these two parameters (as discussed in group-creation), while still only advertising one version / ciphersuite per KeyPackage. </ins> As the "KeyPackage" is a structure which is stored in the Ratchet Tree and updated depending on the evolution of this tree, each time a Commit message is sent and processed.  These states are referred to as "epochs" and are uniquely identified among states of the group by eight-octet epoch values.  When a new group is  <ins> initialized, its initial state epoch is 0x0000000000000000.  Each time a state transition occurs, the epoch number is incremented by one. </ins> [[ OPEN ISSUE: It would be better to have non-linear epochs, in order to tolerate forks in the history.  There is a need to discuss whether


provisioned to the clients within a group.  If proposals with these sender IDs are to be accepted within a group, the members of the group MUST be provisioned by the application with a mapping between <del> these IDs and authorized signing keys.  To ensure consistent handling of external proposals, the application MUST ensure that the members of a group have the same mapping and apply the same policies to external proposals. </del>  An external proposal MUST be sent as an MLSPlaintext object, since the sender will not have the keys necessary to construct an

This stuck out to me while reading because verification is explicitly specified for the 'newmember' sender type in the paragraph above this and not for the 'preconfigured' sender type. I think it would be better to specify this as the verification flow is different for these external proposals i.e. the KeyPackage in the proposal is used for newmember sender types, and the key corresponding to an authorized sender is used for preconfigured senders.

provisioned to the clients within a group.  If proposals with these sender IDs are to be accepted within a group, the members of the group MUST be provisioned by the application with a mapping between  <ins> these IDs and authorized signing keys.  Recipients MUST verify that the MLSPlaintext carrying the Proposal message is validly signed with the corresponding key.  To ensure consistent handling of external proposals, the application MUST ensure that the members of a group have the same mapping and apply the same policies to external proposals. </ins> An external proposal MUST be sent as an MLSPlaintext object, since the sender will not have the keys necessary to construct an


Given these inputs, the derivation of secrets for an epoch proceeds as shown in the following diagram:  7.9. Groups which already have an out-of-band mechanism to generate shared Sign the GroupInfo using the member's private signing key Encrypt the GroupInfo using the key and nonce derived from the <del> "epoch_secret" for the new epoch (see welcoming-new-members) </del>  For each new member in the group: algorithms indicated by the ciphersuite and the HPKE private key corresponding to the GroupSecrets. <del> From the "epoch_secret" in the decrypted GroupSecrets object, </del>  derive the "welcome_secret", "welcome_key", and "welcome_nonce". Use the key and nonce to decrypt the "encrypted_group_info" field. private key MUST be the private key that corresponds to the public key in the node. <del> Use the "epoch_secret" from the GroupSecrets object to generate </del>  the epoch secret and other derived secrets for the current epoch. Set the confirmed transcript hash in the new state to the value of

This PR rearranges the key schedule so that the following values are all fed into the init secret to form epoch secret: The commit secret The PSK (if any) The group context This assures that new joiners know the PSK for the epoch, since they now need to know it to compute the epoch secret. It also moves the group context from the calls under the epoch secret to the derivation of the epoch secret itself, ensuring that everything derived in the epoch has the same context folded in.
The current PSK construct does not authenticate that a member added during the epoch knows the PSK. This is because the PSK is used as an input to the epoch secret computation, and the new joiner is provided with the epoch secret directly. I think that if we reverse the input of the commit secret and the PSK, and provide the intermediate product to the new joiner, we can achieve this property: In other words, would provide the value to the new joiners in the GroupSecrets object, instead of the .
We might want to also provide an optional PSK ID in the GroupSecrets object.
Konrad, Britta and I have been looking at this... Note that a PSK that is generated for a specific group should not be known by another group.
Second that. Also, the PSK ID should be linked to the epoch (or similarly identifiable) such that it is clear what current state is associated with the PSK being used (such as which members have access to the PSK in cases of reinitializing after breaks in group state, branching, etc.).
It doesn't seem like the step between the and the is helping anything. Could we remove it?
I think a number of people want to revisit the key schedule: Britta/Konrad: want to make changes to the PSK part for version upgrades Brendan: (I don't remember exactly) Raphael: wants to harmonise how the GroupState is pulled in At this point it would be good to collect all feedback first and do one big change instead of many small ones.
Me: wants to use the transcript hash instead of the group state because the transcript hash contains strictly more.
Not sure if this is everything, but I know I wanted to: Stop integrating the node id into every level of the application secret tree, to be more friendly to recursive implementations that don't store nodes in a linear array. Integrate context information at specific places, and then just use a hash directly (instead of HKDF) wherever possible. Hashes really do get quite expensive (~10 hashes = 1 ECDSA signature), and HKDF is two or three hashes.
Konrad and I have a draft PR for a number of changes, mostly relating to version upgrades (with group branching support considered if we want that feature in the future). Additional: derive authentication secret per-epoch separate from exporter key (i.e. secrets used in and for the protocol should be derived separately from secrets used beyond the protocol). TL:DR on IETF 104 presentation and other PCS work: authentication secrets can be used either for detection of an active MitM following signature key compromise or simply AS compromise, or in replacement of a TTP.
It seems to me that most of these changes to the key schedule are orthogonal, so maybe everyone should just make a PR for their preferred change? Regarding the changes proposed by NAME I think we should be careful such that we (1) still get the properties we need from the keys and (2) can prove that we actually do. I'm all for optimizing MLS for ease of implementation, but I think it at least warrants a discussion. My take is that 1. does not impact key pseudorandomness but rather the collision resistance properties of that key (and the keys derived from it). From a proof-standpoint (using state-separating proofs ymmv with other techniques) we need that property at least when we next use the corresponding group key in an HKDF-extract function. If we include the transcript (which includes the groupstate) into the key at some point before, it should be doable as well. That being said, it would be much easier if we could make that argument "up the tree" as opposed to just in the end. So I think we have an "ease-of-implementation" vs. "ease-of-proof" trade-off here. My experience with a proof of the TLS key schedule we did recently is that this sort of step complicates the proof quite a bit (although that probably depends on your proof technique). I'd be interested what the other parties think that are doing (or are going to do) proofs. Regarding 2.: Are we not just using HKDF-expand in most cases? If the output of the hash function used to instantiate the hmac used in the HKDF is equal to the desired length of the key, there should only be one hash operation per call to HKDF-expand and it should not be much of a performance penalty if we include context at those points. Please correct me if I'm wrong.
I don't understand the argument about collision resistance here. We're including explicit data in the call to HKDF-Expand (particularly, node id and generation) that's implicitly authenticated by the structure of HKDF-Expand calls used to build the tree. The tree's root is given by the key schedule, which is integrating all of the context you need. If the hash function is weak, how would including more data in its input mitigate an attack? The HKDF-Expand is one HMAC call, and HMAC is two hashes. HMAC would be three hashes if the key were a different length.
My impression was that you wanted to remove the node-id from the HKDF-expand call. Is that not the case? Again, I'm not saying it's impossible to prove uniqueness of the resulting group key if we remove the node-id. I'm just saying it might make it harder to prove (just as leaving them in might make it harder to implement). It's not about the properties of the hash function. It's about ensuring that the output key is distinct from the keys derived in the other nodes. If we assume that the hash function is ideal with regard to collision resistance, the input uniquely determines the output. Thus, if we include the unique node id, we get a unique key, even if the rest of the input is potentially the same. This allows us to assume that the output key is unique, even if the adversary chooses the rest of the input. Ah, I was under the impression that HMAC was just one hash operation. In any case, I'm not an expert regarding properties of hash functions, but my understanding from [1] is that simple hashing is not advisable as a KDF, even if the input is already pseudorandom. Are there any further results on this? I'm happy to go to just a simple hash, but we need to be sure that we actually get good keys as a result. [1] Krawczyk, Cryptographic Extraction and Key Derivation: The HKDF Scheme (URL)
Yes, that's what I want to do. I want to remove now too, that I've thought of it. If the output key for different nodes is the same, that clearly implies you've found a collision in the hash function, so the hash function is weak. That's because you are hashing different things at every point, in particular, and . Arguing "if we assume the hash function is ideal" doesn't make sense to me, because we know the hash function is not ideal. We can generate the bulk of the tree with a hash, and still use HKDF for the chain at the leaves of the ASTree. But perhaps just one call to HKDF-Extract to generate both the key and nonce at once.
But if we're not including that, then how is that "implicitly authenticated by the structure of HKDF-Expand calls used to build the tree" as you said in your earlier reply? I'm talking about arguments in the context of a proof, where the goal is to figure out if we get good keys in the end, under a set of assumptions. One of the assumptions is that we can replace the hash function we use by an ideal one, without an adversary "noticing the difference". It would make the proof easier if we can assume that the output of the key derivation step doesn't collide even if the adversary can control the input key material. Then the inclusion of the node-specific label would give us that guarantee. We might be able to do the proof iteratively across the layers of the tree, but I'm not certain about that. That is why I wouldn't want to throw out that extra information in the key derivation step. I'm happy to explain our proof methodology in more detail to make clear what I'm worried about, but I'm not going to do that in a git issue. I'm not sure that works, because for HKDF-Extract to give you a good (pseudorandom) key, you need the input to be pseudorandom. And I'm not sure there is solid basis to assume that the output of a hash function is indeed a pseudorandom string. If there was, I'd be happy to agree to just replace all HKDF-Expand calls with hash function calls.
A hash is definitely not enough for a key derivation. Just a quick look at the research should be enough to make that statement convincing. There is a reason why we use a KDF for key derivation to achieve sufficient pseudorandomness, and switching it out with a hash as a quick fix will be asking for problems down the line. If we use just a hash, then the output should NOT be called a secret or used as a key, as a general rule (exceptions depending on input). As to removing context from the key derivation: I think that there is an underlying assumption being presented in the argument for removal that the remaining inputs are unique and tied sufficiently to individual nodes already. Hopefully this is the case, but there is no guarantee. Consequently the result of removing them is not simply that proofs may become more difficult, but that the ensuing reductions (for computational proofs) may become less tight. In (quite loose) terms, this means that there are more conditions on the contexts in which security is achieved, and increased possibilities for breaking it.
Take for example, the chain at each leaf of the ASTree. We include a counter in the input to each HKDF-Expand, but the generation counter is equal to how far you've walked in the hash chain. The generation counter = explicit authentication, how far you've walked in the chain = implicit authentication. People have been complaining about the difficulty of proving properties of the key schedule the whole time the wg has existed, trying to make it more expensive and materially less secure, to simplify proofs. In reality, it's one of the few "obviously secure" parts of the protocol and there are still vulnerabilities elsewhere. Proof efforts should be directed to parts of the protocol that are actually difficult for humans to reason about. I think it's very fair to assume that a secure hash function is a random oracle. HKDF is just the output of a hash. We would want to keep HKDF-Expand at the leaves for pragmatic reasons: infinite stream derivation (because key and nonce size are variable), and safely combining the label ("key", "nonce", "secret") with the IKM. I believe Britta's comments are addressed above.

People have been complaining about the difficulty of proving properties of the key schedule the whole time the wg has existed, trying to make it more expensive and materially less secure, to simplify proofs. In reality, it's one of the few "obviously secure" parts of the protocol and there are still vulnerabilities elsewhere. Proof efforts should be directed to parts of the protocol that are actually difficult for humans to reason about. I agree with the assessment that there are many potential vulnerabilities elsewhere that need focus. However, the above statement not only lacks core reasoning on the problem, but indicates misunderstanding of analysis methods and results. You are making an ad hominem argument for ignoring what could have serious implications. See my comment above about tightness of security reductions, which you did not address. Tightness is not about ease of proof, it is about what claims can be proven. Your changes will likely have an effect on that. >I think it's very fair to assume that a secure hash function is a random oracle. HKDF is just the output of a hash. OK, but a decade of cryptographic research disagrees with that general statement. Making wide claims about what you think should be assumed does not progress the decision; it would be helpful if you founded your argument instead on the what the exact inputs are to the different hash uses (and there is a difference based on node height) and explain why you think there is sufficient randomness in the input to not use a KDF. No one so far has said your suggestion is wrong, only that you have not put forth any supporting arguments, i.e. Konrad's comment: >And I'm not sure there is solid basis to assume that the output of a hash function is indeed a pseudorandom string. If there was, I'd be happy to agree to just replace all HKDF-Expand calls with hash function calls. It is in everyone's interest to progress with decisions as scientifically as possible, with descriptive, shared reasoning behind changes - especially ones that have the potential to affect the randomness of keys - and without taking requests for justification personally. Given the extended discussion on this issue, I suggest that it be moved to an interim meeting where it can be given appropriate attention. Engaging further in a git issue thread on the topic is unlikely to achieve satisfactory results, and it clearly warrants further attention.
I'm saying we should de-prioritize provable security in the key schedule for three reasons: It's easy to convince yourself of the key schedule's properties on-sight, both due to its simplicity and how much it has in common with other widely deployed cryptosystems. Even if we optimize the key schedule for provable security, people have indicated that the proofs will still be very difficult and that they may not do them. Existing system-level vulnerabilities are being (and have been) overlooked because of a hyper-focus on proving component-level properties. This way, we can make the key schedule much faster and easier to implement.
Lgtm

Given these inputs, the derivation of secrets for an epoch proceeds as shown in the following diagram: <ins> A number of secrets are derived from the epoch secret for different purposes: </ins> 7.9. Groups which already have an out-of-band mechanism to generate shared Sign the GroupInfo using the member's private signing key Encrypt the GroupInfo using the key and nonce derived from the  <ins> "joiner_secret" for the new epoch (see welcoming-new-members) </ins> For each new member in the group: algorithms indicated by the ciphersuite and the HPKE private key corresponding to the GroupSecrets.  <ins> From the "joiner_secret" in the decrypted GroupSecrets object, </ins> derive the "welcome_secret", "welcome_key", and "welcome_nonce". Use the key and nonce to decrypt the "encrypted_group_info" field. private key MUST be the private key that corresponds to the public key in the node.  <ins> Use the "joiner_secret" from the GroupSecrets object to generate </ins> the epoch secret and other derived secrets for the current epoch. Set the confirmed transcript hash in the new state to the value of