Question:
Just to recap the options as I understand them: a) Today: encryptedsenderdata = AEAD(senderdatakey, senderdatanonce, (groupID, epoch), senderdata) encryptedcontent = AEAD(appkey[i][j], appkey[i][j], (groupID, epoch, encryptedsenderdata), content) b) Sample sender data nonce from ciphertext (saves explicit nonce) encryptedcontent = AEAD(appkey[i][j], appnonce[i][j], (groupID, epoch), content) encryptedsenderdata = AEAD(senderdatakey, sample(encryptedcontent), (groupID, epoch, encryptedcontent), senderdata) c) Drop auth tag from content encryption (saves explicit nonce and content auth tag) encryptedcontent = Enc(appkey[i][j], appnonce[i][j], content) encryptedsenderdata = AEAD(senderdatakey, sample(encryptedcontent), (groupID, epoch, encryptedcontent), senderdata)
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

Set group_id, epoch, content_type and authenticated_data fields from the MLSPlaintext object directly <del> Randomly generate the sender_data_nonce field </del> Identify the key and key generation depending on the content type <del> Encrypt an MLSSenderData object for the encrypted_sender_data field from MLSPlaintext and the key generation Generate and sign an MLSPlaintextTBS object from the MLSPlaintext object </del> Encrypt an MLSCiphertextContent for the ciphertext field using the <del> key identified, the signature, and MLSPlaintext object </del> <ins> key identified and MLSPlaintext object Encrypt the sender data using a key and nonce derived from the "sender_data_secret" for the epoch and a sample of the encrypted MLSCiphertextContent. </ins> <del> Decryption is done by decrypting the metadata, then the message, and then verifying the content signature. </del> <ins> Decryption is done by decrypting the sender data, then the message, and then verifying the content signature. </ins> The following sections describe the encryption and signing processes in detail. 8.1. <del> The "sender data" used to look up the key for the content encryption is encrypted under AEAD using the MLSCiphertext sender_data_nonce and the sender_data_key from the keyschedule.  It is encoded as an object of the following form: MLSSenderData.sender is assumed to be a "member" sender type.  When constructing an MLSSenderData from a Sender object, the sender MUST verify Sender.sender_type is "member" and use Sender.sender for MLSSenderData.sender. 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. The Additional Authenticated Data (AAD) for the SenderData ciphertext computation is its prefix in the MLSCiphertext, namely: When parsing a SenderData struct as part of message decryption, the recipient MUST verify that the sender field represents an occupied leaf in the ratchet tree.  In particular, the sender index value MUST be less than the number of leaves in the tree. 8.2. The signature field in an MLSPlaintext object is computed using the </del> <ins> The "signature" field in an MLSPlaintext object is computed using the </ins> signing private key corresponding to the credential at the leaf in the tree indicated by the sender field.  The signature covers the plaintext metadata and message content, which is all of MLSPlaintext


Question:
Just to recap the options as I understand them: a) Today: encryptedsenderdata = AEAD(senderdatakey, senderdatanonce, (groupID, epoch), senderdata) encryptedcontent = AEAD(appkey[i][j], appkey[i][j], (groupID, epoch, encryptedsenderdata), content) b) Sample sender data nonce from ciphertext (saves explicit nonce) encryptedcontent = AEAD(appkey[i][j], appnonce[i][j], (groupID, epoch), content) encryptedsenderdata = AEAD(senderdatakey, sample(encryptedcontent), (groupID, epoch, encryptedcontent), senderdata) c) Drop auth tag from content encryption (saves explicit nonce and content auth tag) encryptedcontent = Enc(appkey[i][j], appnonce[i][j], content) encryptedsenderdata = AEAD(senderdatakey, sample(encryptedcontent), (groupID, epoch, encryptedcontent), senderdata)
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

GroupContext for the current epoch, so that signatures are specific to a given group and epoch. <del> The ciphertext field of the MLSCiphertext object is produced by </del> <ins> 8.2. The "ciphertext" field of the MLSCiphertext object is produced by </ins> supplying the inputs described below to the AEAD function specified by the ciphersuite in use.  The plaintext input contains content and signature of the MLSPlaintext, plus optional padding.  These values


Question:
Just to recap the options as I understand them: a) Today: encryptedsenderdata = AEAD(senderdatakey, senderdatanonce, (groupID, epoch), senderdata) encryptedcontent = AEAD(appkey[i][j], appkey[i][j], (groupID, epoch, encryptedsenderdata), content) b) Sample sender data nonce from ciphertext (saves explicit nonce) encryptedcontent = AEAD(appkey[i][j], appnonce[i][j], (groupID, epoch), content) encryptedsenderdata = AEAD(senderdatakey, sample(encryptedcontent), (groupID, epoch, encryptedcontent), senderdata) c) Drop auth tag from content encryption (saves explicit nonce and content auth tag) encryptedcontent = Enc(appkey[i][j], appnonce[i][j], content) encryptedsenderdata = AEAD(senderdatakey, sample(encryptedcontent), (groupID, epoch, encryptedcontent), senderdata)
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

contains an object of the following form, with the values used to identify the key and nonce: <del> The ciphertext field of the MLSCiphertext object is produced by supplying these inputs to the AEAD function specified by the ciphersuite in use. </del> <ins> 8.3. The "sender data" used to look up the key for the content encryption is encrypted with the ciphersuite's AEAD with a key and nonce derived from both the "sender_data_secret" and a sample of the encrypted content.  Before being encrypted, the sender data is encoded as an object of the following form: MLSSenderData.sender is assumed to be a "member" sender type.  When constructing an MLSSenderData from a Sender object, the sender MUST verify Sender.sender_type is "member" and use Sender.sender for MLSSenderData.sender. 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-encryption. The key and nonce provided to the AEAD are computed as the KDF of the first "KDF.Nh" bytes of the ciphertext generated in the previous section.  If the length of the ciphertext is less than "KDF.Nh", the whole ciphertext is used without padding.  In pseudocode, the key and nonce are derived as: ``` ciphertext_sample = ciphertext[0..KDF.Nh-1] sender_data_key = ExpandWithLabel(sender_data_secret, "key", ciphertext_sample, AEAD.Nk) sender_data_nonce = ExpandWithLabel(sender_data_secret, "nonce", ciphertext_sample, AEAD.Nn) ``` The Additional Authenticated Data (AAD) for the SenderData ciphertext is all the fields of MLSCiphertext excluding "encrypted_sender_data": When parsing a SenderData struct as part of message decryption, the recipient MUST verify that the sender field represents an occupied leaf in the ratchet tree.  In particular, the sender index value MUST be less than the number of leaves in the tree. </ins> 9.


Question:
Just to recap the options as I understand them: a) Today: encryptedsenderdata = AEAD(senderdatakey, senderdatanonce, (groupID, epoch), senderdata) encryptedcontent = AEAD(appkey[i][j], appkey[i][j], (groupID, epoch, encryptedsenderdata), content) b) Sample sender data nonce from ciphertext (saves explicit nonce) encryptedcontent = AEAD(appkey[i][j], appnonce[i][j], (groupID, epoch), content) encryptedsenderdata = AEAD(senderdatakey, sample(encryptedcontent), (groupID, epoch, encryptedcontent), senderdata) c) Drop auth tag from content encryption (saves explicit nonce and content auth tag) encryptedcontent = Enc(appkey[i][j], appnonce[i][j], content) encryptedsenderdata = AEAD(senderdatakey, sample(encryptedcontent), (groupID, epoch, encryptedcontent), senderdata)
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

The mandatory-to-implement ciphersuite for MLS 1.0 is "MLS10_128_HPKE25519_AES128GCM_SHA256_Ed25519" which uses <del> Curve25519, HKDF over SHA2-256 and AES-128-GCM for HPKE, and AES- 128-GCM with Ed25519 for symmetric encryption and signatures. </del> <ins> Curve25519 for key exchange, AES-128-GCM for HPKE, HKDF over SHA2-256, AES for metadata masking, and Ed25519 for signatures. </ins> Values with the first byte 255 (decimal) are reserved for Private Use.


Question:
The credentials we have defined at the moment only contain a single public key and no information regarding what signature scheme that keypair works with (even though the Spec actually says it should include the signature scheme as well). It would be nice, however, to support potential CredentialTypes that, similar to KeyPackages, include multiple supported signature schemes, each accompanied by a corresponding public key. The Key that should be used in any given group is then determined by the CipherSuite of the group (which includes a Signature Scheme). When choosing a KeyPackage of a new member, it has to be one that contains a Credential which supports that Signature Scheme. Note, that I'm not suggesting that this be the case in all Credentials, or even that we change the BasicCredential. I understand that it's possible to have multiple credentials per identity, but in some authentication settings it can be beneficial to have a 1-to-1 mapping between Credential and identity.
KeyPackages don't support multiple signature schemes? In what scenario is it beneficial?
Ah, right. I think that was the case in the past, but you're right, they don't. Of course it's always possible to just bundle a bunch of certificates, each supporting a different signature scheme and then treat that as your "Multi-Scheme Credential" in the grand scheme of things. However, if you end up rotating them a lot, where you have to sign them and potentially store the whole chain for a longer period of time, being able to support multiple signature schemes in only one credential means less overhead.
Credentials are stored in each group's ratchet tree though, so you're keeping a bunch of unnecessary key material in the group's tree which everyone has to store
That's fair. And it's not something you have to do if you don't want to. I'm just proposing for MLS to support that type of Credential in general.
To support hybrid PQ/standard schemes at any point in the future, allowing for multiple schemes is reasonable (in addition to the above arguments for it). Most applications probably will not use the feature, especially to start with, but providing support for just the option is not going to cause any of the problems noted.
PQ schemes can be used by switching the entire group to a new ciphersuite using those schemes. Requiring everybody in the group to store a bunch of unused key material isn't reasonable
The statement was not about PQ, but hybrid PQ/standard schemes. Those require use to two signature schemes, for which both types of keying material are used.
Then you need a ciphersuite that specifies a hybrid scheme.
It would be ideal to have a ciphersuite specifying a single hybrid scheme, but even that does not imply use of a single credential, unfortunately. In fact, hybrid schemes may rely on two separate credentials, with the ciphersuite specifying the hybrid algorithm for combining them. Since the "main" method of building a solo hybrid credential is under patent, there is also additional motivation for applications to use two.
I concur with Brendan here. To be consistent with our earlier decisions to (a) keep KeyPackages to one ciphersuite and (b) include the signature algorithm in the ciphersuite, we should keep credentials to one signature algorithm as well. Propose closing this PR without merging.
Discussed at 2020-10-06 interim, TODO: Re-add SignatureScheme enum, re-using the TLS signature schemes Clarify that the main job of a Credential is to provide a public key for verifying messages How it provides the credential might be different per credential type e.g., in BasicCredential, it's provided directly, X509Credential, have to parse the leaf certificate
LGTM with a couple nits.

Provider (SP) as described in I-D.ietf-mls-architecture.  In particular, we assume the SP provides the following services: <del> A long-term identity key provider which allows clients to </del> <ins> A long-term signature key provider which allows clients to </ins> authenticate protocol messages in a group. A broadcast channel, for each group, which will relay a message to


Question:
The credentials we have defined at the moment only contain a single public key and no information regarding what signature scheme that keypair works with (even though the Spec actually says it should include the signature scheme as well). It would be nice, however, to support potential CredentialTypes that, similar to KeyPackages, include multiple supported signature schemes, each accompanied by a corresponding public key. The Key that should be used in any given group is then determined by the CipherSuite of the group (which includes a Signature Scheme). When choosing a KeyPackage of a new member, it has to be one that contains a Credential which supports that Signature Scheme. Note, that I'm not suggesting that this be the case in all Credentials, or even that we change the BasicCredential. I understand that it's possible to have multiple credentials per identity, but in some authentication settings it can be beneficial to have a 1-to-1 mapping between Credential and identity.
KeyPackages don't support multiple signature schemes? In what scenario is it beneficial?
Ah, right. I think that was the case in the past, but you're right, they don't. Of course it's always possible to just bundle a bunch of certificates, each supporting a different signature scheme and then treat that as your "Multi-Scheme Credential" in the grand scheme of things. However, if you end up rotating them a lot, where you have to sign them and potentially store the whole chain for a longer period of time, being able to support multiple signature schemes in only one credential means less overhead.
Credentials are stored in each group's ratchet tree though, so you're keeping a bunch of unnecessary key material in the group's tree which everyone has to store
That's fair. And it's not something you have to do if you don't want to. I'm just proposing for MLS to support that type of Credential in general.
To support hybrid PQ/standard schemes at any point in the future, allowing for multiple schemes is reasonable (in addition to the above arguments for it). Most applications probably will not use the feature, especially to start with, but providing support for just the option is not going to cause any of the problems noted.
PQ schemes can be used by switching the entire group to a new ciphersuite using those schemes. Requiring everybody in the group to store a bunch of unused key material isn't reasonable
The statement was not about PQ, but hybrid PQ/standard schemes. Those require use to two signature schemes, for which both types of keying material are used.
Then you need a ciphersuite that specifies a hybrid scheme.
It would be ideal to have a ciphersuite specifying a single hybrid scheme, but even that does not imply use of a single credential, unfortunately. In fact, hybrid schemes may rely on two separate credentials, with the ciphersuite specifying the hybrid algorithm for combining them. Since the "main" method of building a solo hybrid credential is under patent, there is also additional motivation for applications to use two.
I concur with Brendan here. To be consistent with our earlier decisions to (a) keep KeyPackages to one ciphersuite and (b) include the signature algorithm in the ciphersuite, we should keep credentials to one signature algorithm as well. Propose closing this PR without merging.
Discussed at 2020-10-06 interim, TODO: Re-add SignatureScheme enum, re-using the TLS signature schemes Clarify that the main job of a Credential is to provide a public key for verifying messages How it provides the credential might be different per credential type e.g., in BasicCredential, it's provided directly, X509Credential, have to parse the leaf certificate
LGTM with a couple nits.

A member of a group authenticates the identities of other participants by means of credentials issued by some authentication system, like a PKI.  Each type of credential MUST express the <del> following data: </del> <ins> following data in the context of the group it is used with: </ins> <del> The public key of a signature key pair </del> <ins> The public key of a signature key pair matching the SignatureScheme specified by the CipherSuite of the group </ins> <del> The identity of the holder of the private key The signature scheme that the holder will use to sign MLS messages </del> <ins> The identity of the holder of the private keys </ins> Credentials MAY also include information that allows a relying party to verify the identity / signing key binding. <ins> Additionally, Credentials SHOULD specify the signature scheme corresponding to each contained public key. </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


Question:
The credentials we have defined at the moment only contain a single public key and no information regarding what signature scheme that keypair works with (even though the Spec actually says it should include the signature scheme as well). It would be nice, however, to support potential CredentialTypes that, similar to KeyPackages, include multiple supported signature schemes, each accompanied by a corresponding public key. The Key that should be used in any given group is then determined by the CipherSuite of the group (which includes a Signature Scheme). When choosing a KeyPackage of a new member, it has to be one that contains a Credential which supports that Signature Scheme. Note, that I'm not suggesting that this be the case in all Credentials, or even that we change the BasicCredential. I understand that it's possible to have multiple credentials per identity, but in some authentication settings it can be beneficial to have a 1-to-1 mapping between Credential and identity.
KeyPackages don't support multiple signature schemes? In what scenario is it beneficial?
Ah, right. I think that was the case in the past, but you're right, they don't. Of course it's always possible to just bundle a bunch of certificates, each supporting a different signature scheme and then treat that as your "Multi-Scheme Credential" in the grand scheme of things. However, if you end up rotating them a lot, where you have to sign them and potentially store the whole chain for a longer period of time, being able to support multiple signature schemes in only one credential means less overhead.
Credentials are stored in each group's ratchet tree though, so you're keeping a bunch of unnecessary key material in the group's tree which everyone has to store
That's fair. And it's not something you have to do if you don't want to. I'm just proposing for MLS to support that type of Credential in general.
To support hybrid PQ/standard schemes at any point in the future, allowing for multiple schemes is reasonable (in addition to the above arguments for it). Most applications probably will not use the feature, especially to start with, but providing support for just the option is not going to cause any of the problems noted.
PQ schemes can be used by switching the entire group to a new ciphersuite using those schemes. Requiring everybody in the group to store a bunch of unused key material isn't reasonable
The statement was not about PQ, but hybrid PQ/standard schemes. Those require use to two signature schemes, for which both types of keying material are used.
Then you need a ciphersuite that specifies a hybrid scheme.
It would be ideal to have a ciphersuite specifying a single hybrid scheme, but even that does not imply use of a single credential, unfortunately. In fact, hybrid schemes may rely on two separate credentials, with the ciphersuite specifying the hybrid algorithm for combining them. Since the "main" method of building a solo hybrid credential is under patent, there is also additional motivation for applications to use two.
I concur with Brendan here. To be consistent with our earlier decisions to (a) keep KeyPackages to one ciphersuite and (b) include the signature algorithm in the ciphersuite, we should keep credentials to one signature algorithm as well. Propose closing this PR without merging.
Discussed at 2020-10-06 interim, TODO: Re-add SignatureScheme enum, re-using the TLS signature schemes Clarify that the main job of a Credential is to provide a public key for verifying messages How it provides the credential might be different per credential type e.g., in BasicCredential, it's provided directly, X509Credential, have to parse the leaf certificate
LGTM with a couple nits.

A KeyPackage object specifies a ciphersuite that the client supports, as well as providing a public key that others can use for key <del> agreement.  The client's identity key can be updated throughout the </del> <ins> agreement.  The client's signature key can be updated throughout the </ins> lifetime of the group by sending a new KeyPackage with a new <del> identity; the new identity MUST be validated by the authentication service. </del> <ins> signature key; the new signature key MUST be validated by the authentication service. </ins> When used as InitKeys, KeyPackages are intended to be used only once and SHOULD NOT be reused except in case of last resort.  (See


Question:
The credentials we have defined at the moment only contain a single public key and no information regarding what signature scheme that keypair works with (even though the Spec actually says it should include the signature scheme as well). It would be nice, however, to support potential CredentialTypes that, similar to KeyPackages, include multiple supported signature schemes, each accompanied by a corresponding public key. The Key that should be used in any given group is then determined by the CipherSuite of the group (which includes a Signature Scheme). When choosing a KeyPackage of a new member, it has to be one that contains a Credential which supports that Signature Scheme. Note, that I'm not suggesting that this be the case in all Credentials, or even that we change the BasicCredential. I understand that it's possible to have multiple credentials per identity, but in some authentication settings it can be beneficial to have a 1-to-1 mapping between Credential and identity.
KeyPackages don't support multiple signature schemes? In what scenario is it beneficial?
Ah, right. I think that was the case in the past, but you're right, they don't. Of course it's always possible to just bundle a bunch of certificates, each supporting a different signature scheme and then treat that as your "Multi-Scheme Credential" in the grand scheme of things. However, if you end up rotating them a lot, where you have to sign them and potentially store the whole chain for a longer period of time, being able to support multiple signature schemes in only one credential means less overhead.
Credentials are stored in each group's ratchet tree though, so you're keeping a bunch of unnecessary key material in the group's tree which everyone has to store
That's fair. And it's not something you have to do if you don't want to. I'm just proposing for MLS to support that type of Credential in general.
To support hybrid PQ/standard schemes at any point in the future, allowing for multiple schemes is reasonable (in addition to the above arguments for it). Most applications probably will not use the feature, especially to start with, but providing support for just the option is not going to cause any of the problems noted.
PQ schemes can be used by switching the entire group to a new ciphersuite using those schemes. Requiring everybody in the group to store a bunch of unused key material isn't reasonable
The statement was not about PQ, but hybrid PQ/standard schemes. Those require use to two signature schemes, for which both types of keying material are used.
Then you need a ciphersuite that specifies a hybrid scheme.
It would be ideal to have a ciphersuite specifying a single hybrid scheme, but even that does not imply use of a single credential, unfortunately. In fact, hybrid schemes may rely on two separate credentials, with the ciphersuite specifying the hybrid algorithm for combining them. Since the "main" method of building a solo hybrid credential is under patent, there is also additional motivation for applications to use two.
I concur with Brendan here. To be consistent with our earlier decisions to (a) keep KeyPackages to one ciphersuite and (b) include the signature algorithm in the ciphersuite, we should keep credentials to one signature algorithm as well. Propose closing this PR without merging.
Discussed at 2020-10-06 interim, TODO: Re-add SignatureScheme enum, re-using the TLS signature schemes Clarify that the main job of a Credential is to provide a public key for verifying messages How it provides the credential might be different per credential type e.g., in BasicCredential, it's provided directly, X509Credential, have to parse the leaf certificate
LGTM with a couple nits.

The value for hpke_init_key MUST be a public key for the asymmetric encryption scheme defined by cipher_suite.  The whole structure is <del> signed using the client's identity key.  A KeyPackage object with an </del> <ins> signed using the client's signature key.  A KeyPackage object with an </ins> invalid signature field MUST be considered malformed.  The input to the signature computation comprises all of the fields except for the signature field.


Question:
The credentials we have defined at the moment only contain a single public key and no information regarding what signature scheme that keypair works with (even though the Spec actually says it should include the signature scheme as well). It would be nice, however, to support potential CredentialTypes that, similar to KeyPackages, include multiple supported signature schemes, each accompanied by a corresponding public key. The Key that should be used in any given group is then determined by the CipherSuite of the group (which includes a Signature Scheme). When choosing a KeyPackage of a new member, it has to be one that contains a Credential which supports that Signature Scheme. Note, that I'm not suggesting that this be the case in all Credentials, or even that we change the BasicCredential. I understand that it's possible to have multiple credentials per identity, but in some authentication settings it can be beneficial to have a 1-to-1 mapping between Credential and identity.
KeyPackages don't support multiple signature schemes? In what scenario is it beneficial?
Ah, right. I think that was the case in the past, but you're right, they don't. Of course it's always possible to just bundle a bunch of certificates, each supporting a different signature scheme and then treat that as your "Multi-Scheme Credential" in the grand scheme of things. However, if you end up rotating them a lot, where you have to sign them and potentially store the whole chain for a longer period of time, being able to support multiple signature schemes in only one credential means less overhead.
Credentials are stored in each group's ratchet tree though, so you're keeping a bunch of unnecessary key material in the group's tree which everyone has to store
That's fair. And it's not something you have to do if you don't want to. I'm just proposing for MLS to support that type of Credential in general.
To support hybrid PQ/standard schemes at any point in the future, allowing for multiple schemes is reasonable (in addition to the above arguments for it). Most applications probably will not use the feature, especially to start with, but providing support for just the option is not going to cause any of the problems noted.
PQ schemes can be used by switching the entire group to a new ciphersuite using those schemes. Requiring everybody in the group to store a bunch of unused key material isn't reasonable
The statement was not about PQ, but hybrid PQ/standard schemes. Those require use to two signature schemes, for which both types of keying material are used.
Then you need a ciphersuite that specifies a hybrid scheme.
It would be ideal to have a ciphersuite specifying a single hybrid scheme, but even that does not imply use of a single credential, unfortunately. In fact, hybrid schemes may rely on two separate credentials, with the ciphersuite specifying the hybrid algorithm for combining them. Since the "main" method of building a solo hybrid credential is under patent, there is also additional motivation for applications to use two.
I concur with Brendan here. To be consistent with our earlier decisions to (a) keep KeyPackages to one ciphersuite and (b) include the signature algorithm in the ciphersuite, we should keep credentials to one signature algorithm as well. Propose closing this PR without merging.
Discussed at 2020-10-06 interim, TODO: Re-add SignatureScheme enum, re-using the TLS signature schemes Clarify that the main job of a Credential is to provide a public key for verifying messages How it provides the credential might be different per credential type e.g., in BasicCredential, it's provided directly, X509Credential, have to parse the leaf certificate
LGTM with a couple nits.

creator, because they are derived from an authenticated key exchange protocol.  Subsequent leaf keys are known only by their owner. <del> Note that the long-term identity keys used by the protocol MUST be </del> <ins> Note that the long-term signature keys used by the protocol MUST be </ins> distributed by an "honest" authentication service for clients to authenticate their legitimate peers.


Question:
The credentials we have defined at the moment only contain a single public key and no information regarding what signature scheme that keypair works with (even though the Spec actually says it should include the signature scheme as well). It would be nice, however, to support potential CredentialTypes that, similar to KeyPackages, include multiple supported signature schemes, each accompanied by a corresponding public key. The Key that should be used in any given group is then determined by the CipherSuite of the group (which includes a Signature Scheme). When choosing a KeyPackage of a new member, it has to be one that contains a Credential which supports that Signature Scheme. Note, that I'm not suggesting that this be the case in all Credentials, or even that we change the BasicCredential. I understand that it's possible to have multiple credentials per identity, but in some authentication settings it can be beneficial to have a 1-to-1 mapping between Credential and identity.
KeyPackages don't support multiple signature schemes? In what scenario is it beneficial?
Ah, right. I think that was the case in the past, but you're right, they don't. Of course it's always possible to just bundle a bunch of certificates, each supporting a different signature scheme and then treat that as your "Multi-Scheme Credential" in the grand scheme of things. However, if you end up rotating them a lot, where you have to sign them and potentially store the whole chain for a longer period of time, being able to support multiple signature schemes in only one credential means less overhead.
Credentials are stored in each group's ratchet tree though, so you're keeping a bunch of unnecessary key material in the group's tree which everyone has to store
That's fair. And it's not something you have to do if you don't want to. I'm just proposing for MLS to support that type of Credential in general.
To support hybrid PQ/standard schemes at any point in the future, allowing for multiple schemes is reasonable (in addition to the above arguments for it). Most applications probably will not use the feature, especially to start with, but providing support for just the option is not going to cause any of the problems noted.
PQ schemes can be used by switching the entire group to a new ciphersuite using those schemes. Requiring everybody in the group to store a bunch of unused key material isn't reasonable
The statement was not about PQ, but hybrid PQ/standard schemes. Those require use to two signature schemes, for which both types of keying material are used.
Then you need a ciphersuite that specifies a hybrid scheme.
It would be ideal to have a ciphersuite specifying a single hybrid scheme, but even that does not imply use of a single credential, unfortunately. In fact, hybrid schemes may rely on two separate credentials, with the ciphersuite specifying the hybrid algorithm for combining them. Since the "main" method of building a solo hybrid credential is under patent, there is also additional motivation for applications to use two.
I concur with Brendan here. To be consistent with our earlier decisions to (a) keep KeyPackages to one ciphersuite and (b) include the signature algorithm in the ciphersuite, we should keep credentials to one signature algorithm as well. Propose closing this PR without merging.
Discussed at 2020-10-06 interim, TODO: Re-add SignatureScheme enum, re-using the TLS signature schemes Clarify that the main job of a Credential is to provide a public key for verifying messages How it provides the credential might be different per credential type e.g., in BasicCredential, it's provided directly, X509Credential, have to parse the leaf certificate
LGTM with a couple nits.

The second form considers authentication with respect to the sender, meaning the group members can verify that a message originated from a particular member of the group.  This property is provided by digital <del> signatures on the messages under identity keys. </del> <ins> signatures on the messages under signature keys. </ins> 14.3.


Question:
The credentials we have defined at the moment only contain a single public key and no information regarding what signature scheme that keypair works with (even though the Spec actually says it should include the signature scheme as well). It would be nice, however, to support potential CredentialTypes that, similar to KeyPackages, include multiple supported signature schemes, each accompanied by a corresponding public key. The Key that should be used in any given group is then determined by the CipherSuite of the group (which includes a Signature Scheme). When choosing a KeyPackage of a new member, it has to be one that contains a Credential which supports that Signature Scheme. Note, that I'm not suggesting that this be the case in all Credentials, or even that we change the BasicCredential. I understand that it's possible to have multiple credentials per identity, but in some authentication settings it can be beneficial to have a 1-to-1 mapping between Credential and identity.
KeyPackages don't support multiple signature schemes? In what scenario is it beneficial?
Ah, right. I think that was the case in the past, but you're right, they don't. Of course it's always possible to just bundle a bunch of certificates, each supporting a different signature scheme and then treat that as your "Multi-Scheme Credential" in the grand scheme of things. However, if you end up rotating them a lot, where you have to sign them and potentially store the whole chain for a longer period of time, being able to support multiple signature schemes in only one credential means less overhead.
Credentials are stored in each group's ratchet tree though, so you're keeping a bunch of unnecessary key material in the group's tree which everyone has to store
That's fair. And it's not something you have to do if you don't want to. I'm just proposing for MLS to support that type of Credential in general.
To support hybrid PQ/standard schemes at any point in the future, allowing for multiple schemes is reasonable (in addition to the above arguments for it). Most applications probably will not use the feature, especially to start with, but providing support for just the option is not going to cause any of the problems noted.
PQ schemes can be used by switching the entire group to a new ciphersuite using those schemes. Requiring everybody in the group to store a bunch of unused key material isn't reasonable
The statement was not about PQ, but hybrid PQ/standard schemes. Those require use to two signature schemes, for which both types of keying material are used.
Then you need a ciphersuite that specifies a hybrid scheme.
It would be ideal to have a ciphersuite specifying a single hybrid scheme, but even that does not imply use of a single credential, unfortunately. In fact, hybrid schemes may rely on two separate credentials, with the ciphersuite specifying the hybrid algorithm for combining them. Since the "main" method of building a solo hybrid credential is under patent, there is also additional motivation for applications to use two.
I concur with Brendan here. To be consistent with our earlier decisions to (a) keep KeyPackages to one ciphersuite and (b) include the signature algorithm in the ciphersuite, we should keep credentials to one signature algorithm as well. Propose closing this PR without merging.
Discussed at 2020-10-06 interim, TODO: Re-add SignatureScheme enum, re-using the TLS signature schemes Clarify that the main job of a Credential is to provide a public key for verifying messages How it provides the credential might be different per credential type e.g., in BasicCredential, it's provided directly, X509Credential, have to parse the leaf certificate
LGTM with a couple nits.

reveal previous message or root keys.  Post-compromise security is provided by Commit operations, in which a new root key is generated from the latest ratcheting tree.  If the adversary cannot derive the <del> updated root key after an Commit operation, it cannot compute any </del> <ins> updated root key after a Commit operation, it cannot compute any </ins> derived secrets. In the case where the client could have been compromised (device


Question:
cc NAME
NAME is the build expected to fail?
No, but the problem seem unrelated to your change, so we are all good.
Looks like it was a transient failure. Re-running resulted in success.
NAME points out that the HPKE version is not pinned. This is probably needed for interop. Version -07 should come sometime this week, so that seems like a perfectly fine version to use. I can send a PR when that version is cut.
Fixed by

A signed object describing a client's identity and capabilities, and including a hybrid public-key encryption (HPKE I-D.irtf-cfrg- <del> hpke ) public key that can be used to encrypt to that client. </del> <ins> hpke) public key that can be used to encrypt to that client. </ins> A key package that is prepublished by a client, which other clients can use to introduce the client to a new group.


Question:
cc NAME
NAME is the build expected to fail?
No, but the problem seem unrelated to your change, so we are all good.
Looks like it was a transient failure. Re-running resulted in success.
NAME points out that the HPKE version is not pinned. This is probably needed for interop. Version -07 should come sometime this week, so that seems like a perfectly fine version to use. I can send a PR when that version is cut.
Fixed by

A signature algorithm <del> The HPKE parameters are used to instantiate HPKE I-D.irtf-cfrg-hpke for the purpose of public-key encryption.  The "DeriveKeyPair" function associated to the KEM for the ciphersuite maps octet strings to HPKE key pairs. </del> <ins> MLS uses draft-07 of HPKE I-D.irtf-cfrg-hpke for public-key encryption.  The "DeriveKeyPair" function associated to the KEM for the ciphersuite maps octet strings to HPKE key pairs. </ins> Ciphersuites are represented with the CipherSuite type.  HPKE public keys are opaque values in a format defined by the underlying protocol


Question:
I noticed a bunch of typos in the "IANA Considerations" section: the "ciphersuites" and "credentials" were probably copy-pasted from the "extension" one, and some parts were not changed correctly. This PR fixes these typos.
Discussed 20210526 Interim: editorial will be taken up by editors.
Thanks!

Name: The name of the ciphersuite <del> 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. </del> <ins> Recommended: Whether support for this ciphersuite 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> Reference: The document where this ciphersuite is defined


Question:
I noticed a bunch of typos in the "IANA Considerations" section: the "ciphersuites" and "credentials" were probably copy-pasted from the "extension" one, and some parts were not changed correctly. This PR fixes these typos.
Discussed 20210526 Interim: editorial will be taken up by editors.
Thanks!

16.3. This registry lists identifiers for types of credentials that can be <del> used for authentication in the MLS protocol.  The extension type field is two bytes wide, so valid extension type values are in the </del> <ins> used for authentication in the MLS protocol.  The credential type field is two bytes wide, so valid credential type values are in the </ins> range 0x0000 to 0xffff. Template:


Question:
I noticed a bunch of typos in the "IANA Considerations" section: the "ciphersuites" and "credentials" were probably copy-pasted from the "extension" one, and some parts were not changed correctly. This PR fixes these typos.
Discussed 20210526 Interim: editorial will be taken up by editors.
Thanks!

Name: The name of the credential type <del> Recommended: Whether support for this extension is recommended by </del> <ins> Recommended: Whether support for this credential is recommended by </ins> 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. <del> Reference: The document where this extension is defined </del> <ins> Reference: The document where this credential is defined </ins> Initial contents:


Question:
In , NAME noted that the concatenation approach used to combine multiple PSKs is not well validated. At the 2021-10-04 interim, we discussed moving from this approach to a "cascaded HKDF" approach, with the idea of doing the same thing here as the main key schedule. This PR implements that suggestion. One nice implication of the algorithm here is that when there are no PSKs, it produces the zero vector as a degenerate case. So you could implement it pretty cleanly:
The spec currently doesn't distinguish much between an external and a member commit w.r.t. how proposals by reference are handled. In theory, the server could keep track of proposals and send the external committer all proposals that were sent up until the time they want to commit externally. However, there are a few difficulties in committing proposals by reference in an external commit. Membership tags: The external committer doesn't have the ability to verify membership tags on proposals. This is probably not a show-stopper, as proposals still need to be signed by a group member, but if nothing else, this should be noted in the spec. Leaf positioning: The spec was changed such that the committer is added into the tree without an Add proposal being present. If there are other Add (and Remove) proposals present, it is unclear in what order they should be processed. Should the committer be added when the path is processed? Or should it still be processed as if it were an Add? There are probably other things that can go wrong when committing referenced proposals externally, but those are the first that come to mind. The simplest solution would be to simply ignore proposals by reference, although this would break the current principle that a commit must always include all valid proposals that were sent during the epoch. The consequence would be that Parties that sent proposals will have to re-send them if the epoch is ended by an external commit. It should be noted, that a Preconfigured sender will have no way to detect if their proposal was committed or not.
I'm not sure your worries here are very real: Membership tags: I don't think the membership tag issue matters, because the existing members will reject the proposal (and thus the commit) if the membership tag is invalid; at worst the joiner gets themselves wedged into a group by themselves. And as you say, they're signed, and they keys can be tied back to the tree hash in the GroupInfo. Leaf positioning: We need to clear this up regardless of proposals-by-reference. It seems like basically having a synthetic Add at the beginning or end of the proposal list would work fine. Preconfigured proposer: This is a problem only when Commits are encrypted, so it's not a problem in the external commit case (since an external Commit cannot be encrypted). Nonetheless, the point that an external joiner can't fully verify a Proposal seems to argue pretty strongly for forbidding proposals by reference. I don't think this even violates the semantic of "include all valid proposals received in the epoch", since (1) that requirement is receiver oriented (it's not enough for the proposals to have been sent) and (2) the external joiner can't tell if the proposals or valid. Overall, I would lean slightly toward removing proposals by reference. And if we do that, it might even make sense to just define an ExternalCommit message that packages together the few things that are still allowed.
I'm not sure I understand how the receiver-orientation of the "all valid proposals must be included" rule matters to its application to this case. If we remove the requirement for external commits to include proposals by reference and don't soften that rule to exclude external commits, receivers will consider all external commits invalid in the presence of valid pending proposals by reference. In any case, I'm also in favor of keeping it simple and restrict the External Commit to a few, well defined components that do not include any proposals by reference.
Discussion on working call: "No proposals by reference" kind of follows from "all valid proposals", since the joiner can't necessarily determine that proposals are valid Maybe allow attempts? Might not get feedback to retry, though. Whole point of external commit is to allow async. Agreement to prohibit proposals by reference in external Commit
I believe there's a small mismatch between the text and the Key Schedule figure. The text says that if there are no PSK proposals, the should be a zero-length string, while the Key Schedule figure seems to indicate that it's a string of zeros of length .
Fixed in
One brief comment, otherwise looks good!

in the "PreSharedKeyID" object.  Specifically, "psk_secret" is computed as follows: <del> The "index" field in "PSKLabel" corresponds to the index of the PSK in the "psk" array, while the "count" field contains the total number of PSKs. </del> <ins> Here "0" represents the all-zero vector of length KDF.Nh.  The "index" field in "PSKLabel" corresponds to the index of the PSK in the "psk" array, while the "count" field contains the total number of PSKs.  In other words, the PSKs are chained together with KDF.Extract invocations, as follows: In particular, if there are no PreSharedKey proposals in a given Commit, then the resulting "psk_secret" is "psk_secret_[0]", the all- zero vector. </ins> 8.3.


Question:
In , NAME noted that the concatenation approach used to combine multiple PSKs is not well validated. At the 2021-10-04 interim, we discussed moving from this approach to a "cascaded HKDF" approach, with the idea of doing the same thing here as the main key schedule. This PR implements that suggestion. One nice implication of the algorithm here is that when there are no PSKs, it produces the zero vector as a degenerate case. So you could implement it pretty cleanly:
The spec currently doesn't distinguish much between an external and a member commit w.r.t. how proposals by reference are handled. In theory, the server could keep track of proposals and send the external committer all proposals that were sent up until the time they want to commit externally. However, there are a few difficulties in committing proposals by reference in an external commit. Membership tags: The external committer doesn't have the ability to verify membership tags on proposals. This is probably not a show-stopper, as proposals still need to be signed by a group member, but if nothing else, this should be noted in the spec. Leaf positioning: The spec was changed such that the committer is added into the tree without an Add proposal being present. If there are other Add (and Remove) proposals present, it is unclear in what order they should be processed. Should the committer be added when the path is processed? Or should it still be processed as if it were an Add? There are probably other things that can go wrong when committing referenced proposals externally, but those are the first that come to mind. The simplest solution would be to simply ignore proposals by reference, although this would break the current principle that a commit must always include all valid proposals that were sent during the epoch. The consequence would be that Parties that sent proposals will have to re-send them if the epoch is ended by an external commit. It should be noted, that a Preconfigured sender will have no way to detect if their proposal was committed or not.
I'm not sure your worries here are very real: Membership tags: I don't think the membership tag issue matters, because the existing members will reject the proposal (and thus the commit) if the membership tag is invalid; at worst the joiner gets themselves wedged into a group by themselves. And as you say, they're signed, and they keys can be tied back to the tree hash in the GroupInfo. Leaf positioning: We need to clear this up regardless of proposals-by-reference. It seems like basically having a synthetic Add at the beginning or end of the proposal list would work fine. Preconfigured proposer: This is a problem only when Commits are encrypted, so it's not a problem in the external commit case (since an external Commit cannot be encrypted). Nonetheless, the point that an external joiner can't fully verify a Proposal seems to argue pretty strongly for forbidding proposals by reference. I don't think this even violates the semantic of "include all valid proposals received in the epoch", since (1) that requirement is receiver oriented (it's not enough for the proposals to have been sent) and (2) the external joiner can't tell if the proposals or valid. Overall, I would lean slightly toward removing proposals by reference. And if we do that, it might even make sense to just define an ExternalCommit message that packages together the few things that are still allowed.
I'm not sure I understand how the receiver-orientation of the "all valid proposals must be included" rule matters to its application to this case. If we remove the requirement for external commits to include proposals by reference and don't soften that rule to exclude external commits, receivers will consider all external commits invalid in the presence of valid pending proposals by reference. In any case, I'm also in favor of keeping it simple and restrict the External Commit to a few, well defined components that do not include any proposals by reference.
Discussion on working call: "No proposals by reference" kind of follows from "all valid proposals", since the joiner can't necessarily determine that proposals are valid Maybe allow attempts? Might not get feedback to retry, though. Whole point of external commit is to allow async. Agreement to prohibit proposals by reference in external Commit
I believe there's a small mismatch between the text and the Key Schedule figure. The text says that if there are no PSK proposals, the should be a zero-length string, while the Key Schedule figure seems to indicate that it's a string of zeros of length .
Fixed in
One brief comment, otherwise looks good!

In this case, the new ratchet tree is the same as the provisional ratchet tree. <del> If one or more PreSharedKey proposals are part of the commit, derive the "psk_secret" as specified in pre-shared-keys, where the </del> <ins> Derive the "psk_secret" as specified in pre-shared-keys, where the </ins> order of PSKs in the derivation corresponds to the order of <del> PreSharedKey proposals in the "proposals" vector.  Otherwise, set "psk_secret" to a zero-length octet string. </del> <ins> PreSharedKey proposals in the "proposals" vector. </ins> Construct an MLSPlaintext object containing the Commit object. Sign the MLSPlaintext using the old GroupContext as context.


Question:
In , NAME noted that the concatenation approach used to combine multiple PSKs is not well validated. At the 2021-10-04 interim, we discussed moving from this approach to a "cascaded HKDF" approach, with the idea of doing the same thing here as the main key schedule. This PR implements that suggestion. One nice implication of the algorithm here is that when there are no PSKs, it produces the zero vector as a degenerate case. So you could implement it pretty cleanly:
The spec currently doesn't distinguish much between an external and a member commit w.r.t. how proposals by reference are handled. In theory, the server could keep track of proposals and send the external committer all proposals that were sent up until the time they want to commit externally. However, there are a few difficulties in committing proposals by reference in an external commit. Membership tags: The external committer doesn't have the ability to verify membership tags on proposals. This is probably not a show-stopper, as proposals still need to be signed by a group member, but if nothing else, this should be noted in the spec. Leaf positioning: The spec was changed such that the committer is added into the tree without an Add proposal being present. If there are other Add (and Remove) proposals present, it is unclear in what order they should be processed. Should the committer be added when the path is processed? Or should it still be processed as if it were an Add? There are probably other things that can go wrong when committing referenced proposals externally, but those are the first that come to mind. The simplest solution would be to simply ignore proposals by reference, although this would break the current principle that a commit must always include all valid proposals that were sent during the epoch. The consequence would be that Parties that sent proposals will have to re-send them if the epoch is ended by an external commit. It should be noted, that a Preconfigured sender will have no way to detect if their proposal was committed or not.
I'm not sure your worries here are very real: Membership tags: I don't think the membership tag issue matters, because the existing members will reject the proposal (and thus the commit) if the membership tag is invalid; at worst the joiner gets themselves wedged into a group by themselves. And as you say, they're signed, and they keys can be tied back to the tree hash in the GroupInfo. Leaf positioning: We need to clear this up regardless of proposals-by-reference. It seems like basically having a synthetic Add at the beginning or end of the proposal list would work fine. Preconfigured proposer: This is a problem only when Commits are encrypted, so it's not a problem in the external commit case (since an external Commit cannot be encrypted). Nonetheless, the point that an external joiner can't fully verify a Proposal seems to argue pretty strongly for forbidding proposals by reference. I don't think this even violates the semantic of "include all valid proposals received in the epoch", since (1) that requirement is receiver oriented (it's not enough for the proposals to have been sent) and (2) the external joiner can't tell if the proposals or valid. Overall, I would lean slightly toward removing proposals by reference. And if we do that, it might even make sense to just define an ExternalCommit message that packages together the few things that are still allowed.
I'm not sure I understand how the receiver-orientation of the "all valid proposals must be included" rule matters to its application to this case. If we remove the requirement for external commits to include proposals by reference and don't soften that rule to exclude external commits, receivers will consider all external commits invalid in the presence of valid pending proposals by reference. In any case, I'm also in favor of keeping it simple and restrict the External Commit to a few, well defined components that do not include any proposals by reference.
Discussion on working call: "No proposals by reference" kind of follows from "all valid proposals", since the joiner can't necessarily determine that proposals are valid Maybe allow attempts? Might not get feedback to retry, though. Whole point of external commit is to allow async. Agreement to prohibit proposals by reference in external Commit
I believe there's a small mismatch between the text and the Key Schedule figure. The text says that if there are no PSK proposals, the should be a zero-length string, while the Key Schedule figure seems to indicate that it's a string of zeros of length .
Fixed in
One brief comment, otherwise looks good!

Update the confirmed and interim transcript hashes using the new Commit, and generate the new GroupContext. <del> If the "proposals" vector contains any PreSharedKey proposals, derive the "psk_secret" as specified in pre-shared-keys, where the </del> <ins> Derive the "psk_secret" as specified in pre-shared-keys, where the </ins> order of PSKs in the derivation corresponds to the order of <del> PreSharedKey proposals in the "proposals" vector.  Otherwise, set "psk_secret" to 0. </del> <ins> PreSharedKey proposals in the "proposals" vector. </ins> Use the "init_secret" from the previous epoch, the "commit_secret" and the "psk_secret" as defined in the previous steps, and the new


Question: The definition of blank node needs to move ahead into ## Ratchet Tree Terminology as it is used in the section immediately following (## Views of a Ratchet Tree).

Each leaf is given an _index_ (or _leaf index_), starting at "0" from the left to "n-1" at the right. <ins> Finally, a node in the tree may also be _blank_, indicating that no value is present at that node (i.e. no keying material).  This is often the case when a leaf was recently removed from the tree. </ins> There are multiple ways that an implementation might represent a ratchet tree in memory.  For example, left-balanced binary trees can be represented as an array of nodes, with node relationships computed


Question: The definition of blank node needs to move ahead into ## Ratchet Tree Terminology as it is used in the section immediately following (## Views of a Ratchet Tree).

The conditions under which each of these values must or must not be present are laid out in views. <del> A node in the tree may also be _blank_, indicating that no value is present at that node.  The _resolution_ of a node is an ordered list of non-blank nodes that collectively cover all non-blank descendants of the node.  The resolution of a non-blank node with no unmerged leaves is just the node itself.  More generally, the resolution of a node is effectively a depth-first, left-first enumeration of the nearest non-blank nodes below the node: </del> <ins> The _resolution_ of a node is an ordered list of non-blank nodes that collectively cover all non-blank descendants of the node.  The resolution of a non-blank node with no unmerged leaves is just the node itself.  More generally, the resolution of a node is effectively a depth-first, left-first enumeration of the nearest non-blank nodes below the node: </ins> The resolution of a non-blank node comprises the node itself, followed by its list of unmerged leaves, if any


Question:
This PR introduces a notion of a proposal type being "path safe", in the sense that it is safe for the path to be omitted. (Better phrasing welcome!) The field is required default, and allowed to be omitted only if all the proposals in the Commit are path-safe.
Thanks for taking care of this fix! One suggestion: Instead of having a global list of which types are path safe and which ones aren't, we could make it a property of the proposal. Maybe something like this: Regarding the name, the only thing I can think of is that we could invert the property and call it : In fact, I think I'd be slightly in favor of the latter proposal.
Discussion on virtual interim: OK to keep using type TODO(NAME "path safe" -> "path required" Otherwise clear to merge
The authoritative definition (in Section ) of when to include a is not the same as the summary below. For example, if an implementation adds a custom proposal type, according to the summary, a path is required, whereas according to the definition above it, no path is required. Ultimately, it depends on the nature of the proposal and the desired security guarantees if a path is required. However, I think the safer option is to require a path by default and maybe leave it to the definition of the respective custom proposal to explicitly specify if a path is not required.

"lazy" version of this operation, where only the leaf changes and intermediate nodes are blanked out. <del> The "path" field of a Commit message MUST be populated if the Commit covers at least one Update or Remove proposal.  The "path" field MUST also be populated if the Commit covers no proposals at all (i.e., if the proposals vector is empty).  The "path" field MAY be omitted if the Commit covers only Add proposals.  In pseudocode, the logic for validating a Commit is as follows: </del> <ins> By default, the "path" field of a Commit MUST be populated.  The "path" field MAY be omitted if (a) it covers at least one proposal and (b) none proposals covered by the Commit are of "path required" types.  A proposal type requires a path if it cannot change the group membership in a way that requires the forward secrecy and post- compromise security guarantees that an UpdatePath provides.  The only proposal types defined in this document that do not require a path are: "add" "psk" "app_ack" "reinit" New proposal types MUST state whether they require a path.  If any instance of a proposal type requires a path, then the proposal type requires a path.  This attribute of a proposal type is reflected in the "Path Required" field of the proposal type registry defined in mls-proposal-types. Update and Remove proposals are the clearest examples of proposals that require a path.  An UpdatePath is required to evict the removed member or the old appearance of the updated member. In pseudocode, the logic for validating the "path" field of a Commit is as follows: </ins> To summarize, a Commit can have three different configurations, with different uses:


Question:
This PR introduces a notion of a proposal type being "path safe", in the sense that it is safe for the path to be omitted. (Better phrasing welcome!) The field is required default, and allowed to be omitted only if all the proposals in the Commit are path-safe.
Thanks for taking care of this fix! One suggestion: Instead of having a global list of which types are path safe and which ones aren't, we could make it a property of the proposal. Maybe something like this: Regarding the name, the only thing I can think of is that we could invert the property and call it : In fact, I think I'd be slightly in favor of the latter proposal.
Discussion on virtual interim: OK to keep using type TODO(NAME "path safe" -> "path required" Otherwise clear to merge
The authoritative definition (in Section ) of when to include a is not the same as the summary below. For example, if an implementation adds a custom proposal type, according to the summary, a path is required, whereas according to the definition above it, no path is required. Ultimately, it depends on the nature of the proposal and the desired security guarantees if a path is required. However, I think the safer option is to require a path by default and maybe leave it to the definition of the respective custom proposal to explicitly specify if a path is not required.

committer's contribution to the group and provides PCS with regard to the committer. <del> A "partial" Commit that references Add, PreSharedKey, or ReInit proposals but where the path is empty.  Such a commit doesn't provide PCS with regard to the committer. </del> <ins> A "partial" Commit that references proposals that do not require a path, and where the path is empty.  Such a commit doesn't provide PCS with regard to the committer. </ins> A "full" Commit that references proposals of any type, which provides FS with regard to any removed members and PCS for the


Question:
Based on , it should be made explicit that sub-group branching requires a fresh key package for each member. I think there is still a conflict of wording here that I would like feedback on because of this line (which is what triggered me thinking there was a bug)
Thanks for the PR and thanks for digging into this. I think what the sentence is meant to say is that the receiver MUST check that the members of the new group are a subset of the members of the old group at that specific epoch. Since we're using new LeafNodes in the new group and we don't have a fixed notion of identity (at least not within MLS), we have to refer to the application/the AS to ensure that the identity expressed by the credential in each LeafNode in the subgroup has a corresponding identity (i.e. one that represents the same party) expressed by a credential in a LeafNode in the original group. Maybe something like: This leaves the possibility of a collision, where the identifiers in a given leaf in the new group would be valid updates to more than one member in the old group. This should probably be forbidden in general, although outside of this case it's not really a problem, because updates are always specific to one leaf. Alternatively, we could somehow include LeafNodeRefs that specifically indicate which member represents which other member. Or just leave it up to the application to sort this all out.
Curious what you think about saying the the credential has to be equal? We can determine equality of the credential itself just by doing tls serialization.
Sure, requiring the credential to be exactly the same would be a simple way to figure out if it's the same party in both groups. However, I can imagine that some applications will want distinct credentials for each group. If we mandate that credentials are the same, this would force the application to use the same credential in every key package/LeafNode. Since subgroups can potentially be branched at any time and in any group, all key packages that a party publishes then have to have the same credential as all of the groups that party is in, so another party branching a subgroup can get hold of a KeyPackage with the same credential as the group the subgroup is branched off of. Personally, I'd prefer asking the application to check that the identities of the new group are a subset of the identities of the original group.
I can see that being an issue, although I am somewhat worried about us not being opinionated in same way about how to determine equality here, mainly because of interop type things. We punt on how to validate the credentials as well in general so I guess this is in line with prior decisions
Hey NAME and NAME check the change I just posted. NAME and I put that together, it's a bit tough to get a generic definition of equivalency.
There is currently a logical flaw in the subgroup branching flow. In order to branch, you need to compose a message . A Welcome message includes which is defined as follows: The problem with this is that the field of type cannot be made for existing members of the group because the leaf nodes are now types. is now only used to add new members to the group, so we can't reuse this for sub group initialization.
I'm not sure I understand correctly. Are you suggesting when branching a group off of another group, one re-uses the leaves of the old group? If so, that's not how it's meant to work. The new group created in the process of subgroup branching should be independent from the "parent" group safe for the PSK, which all member have to provide upon creation of the group. In fact one should never use the same key material in the leaves of multiple groups except when the supply of key packages that a party provides via the DS is exhausted (see Section 17.4).
That was part of my question. When there were key packages in the leaves, you theoretically could fork the group from a given point in time and reuse the current tree, but I wasn't sure if that was a proper solution or not. Sounds like it isn't, but either way, I think there should be clarification in the document about this. Let me write up a quick PR on it.
NAME opened URL Would like your feedback there. We have conflicting wording here as well. What threw me off in the first place was the line indicating that LeafNodes should be the same in the sub-group, and if we are fetching new key packages they will not be the same.
NAME Konrad, could you safely derive new keys for the members of the subgroup instead? If so, that would be an enormous increase in efficiency and (especially in the federated case) make group creation timing consistent enough to do dynamically.
NAME Nice idea! But it's tricky, since we're dealing with public key pairs that the creator of the new group doesn't know the private keys for. If we were only using HPKE schemes that support updateable public key encryption, the creator of the new group could indeed update the key in the existing group and send the "delta" encrypted to each corresponding member (which would still represent an overhead, but we might be able to optimize this somehow). This is similar to the RTreeKEM mechanism that NAME et al. proposed at some point. To go all the way here, we'd have to do the same for the public key material in the credentials as well, which is not something we had considered before. It would be a new primitive, something like an updateable signature scheme? Probably something to consider for HPKE/MLS 2.0. We could also allow the re-use of LeadNodes for this particular case. It would be the simpler solution and I don't think it would be catastrophic, since we have to assume that LeafNode (or KeyPackage) key material is re-used in the "last resort" scenario anyway, but this would definitely be something we should discuss on the mailing list, as it would break a pretty basic assumption we've made until now.
Just catching up on this. NAME nice catch here. It seems like we have two options here: Require new KeyPackages (as in ) Invent some way to make a new group based on LeafNodes alone I probably agree that we should do (1) in the short term, but (2) also has some appeal -- it doesn't feel right to require new KeyPackages for folks you're already in touch with -- despite it probably being more spec text. So here's a sketch of (2) in the interest of completeness. Suppose we defined a message of the following form: ... which would pretty closely parallel to Commit. You would send it in MLSMessage and require a in MLSMessageAuth. Where Commit instructs the recipient to create a new epoch from the current one within the same group, Branch would instruct the recipient to create a new epoch that is the first epoch of a new group. In both cases, you would form an epoch / GroupContext as specified, then use the to ratchet into the new epoch. I'm pretty sure that would work and have the expected security properties, though obviously formal analysis would be appreciated. The main awkardness that occurs to me is that any members joining after the branch would have to provide new key packages. And obviously, it would require a fair bit more spec text. All that said, though, the exercise of working all that out has increased my impression that we should do (1) for now. If we want something like (2) later, it can be done pretty cleanly in an extension.
Fixed by
Well put!

12.3. A new group can be formed from a subset of an existing group's <del> members, using the same parameters as the old group.  The creator of the group indicates this situation by including a PreSharedKey of type "resumption" with usage "branch" in the Welcome message that creates the branched subgroup. </del> <ins> members, using the same parameters as the old group. A member can create a sub-group by performing the following steps: Determine a subset of existing members that should be a part of the sub-group. Create a new tree for the sub-group by fetching a new KeyPackage for each existing member that should be included in the sub-group. Create a Welcome message that includes a PreSharedKey of type "resumption" with usage "branch". </ins> A client receiving a Welcome including a PreSharedKey of type "resumption" with usage "branch" MUST verify that the new group


Question:
Based on , it should be made explicit that sub-group branching requires a fresh key package for each member. I think there is still a conflict of wording here that I would like feedback on because of this line (which is what triggered me thinking there was a bug)
Thanks for the PR and thanks for digging into this. I think what the sentence is meant to say is that the receiver MUST check that the members of the new group are a subset of the members of the old group at that specific epoch. Since we're using new LeafNodes in the new group and we don't have a fixed notion of identity (at least not within MLS), we have to refer to the application/the AS to ensure that the identity expressed by the credential in each LeafNode in the subgroup has a corresponding identity (i.e. one that represents the same party) expressed by a credential in a LeafNode in the original group. Maybe something like: This leaves the possibility of a collision, where the identifiers in a given leaf in the new group would be valid updates to more than one member in the old group. This should probably be forbidden in general, although outside of this case it's not really a problem, because updates are always specific to one leaf. Alternatively, we could somehow include LeafNodeRefs that specifically indicate which member represents which other member. Or just leave it up to the application to sort this all out.
Curious what you think about saying the the credential has to be equal? We can determine equality of the credential itself just by doing tls serialization.
Sure, requiring the credential to be exactly the same would be a simple way to figure out if it's the same party in both groups. However, I can imagine that some applications will want distinct credentials for each group. If we mandate that credentials are the same, this would force the application to use the same credential in every key package/LeafNode. Since subgroups can potentially be branched at any time and in any group, all key packages that a party publishes then have to have the same credential as all of the groups that party is in, so another party branching a subgroup can get hold of a KeyPackage with the same credential as the group the subgroup is branched off of. Personally, I'd prefer asking the application to check that the identities of the new group are a subset of the identities of the original group.
I can see that being an issue, although I am somewhat worried about us not being opinionated in same way about how to determine equality here, mainly because of interop type things. We punt on how to validate the credentials as well in general so I guess this is in line with prior decisions
Hey NAME and NAME check the change I just posted. NAME and I put that together, it's a bit tough to get a generic definition of equivalency.
There is currently a logical flaw in the subgroup branching flow. In order to branch, you need to compose a message . A Welcome message includes which is defined as follows: The problem with this is that the field of type cannot be made for existing members of the group because the leaf nodes are now types. is now only used to add new members to the group, so we can't reuse this for sub group initialization.
I'm not sure I understand correctly. Are you suggesting when branching a group off of another group, one re-uses the leaves of the old group? If so, that's not how it's meant to work. The new group created in the process of subgroup branching should be independent from the "parent" group safe for the PSK, which all member have to provide upon creation of the group. In fact one should never use the same key material in the leaves of multiple groups except when the supply of key packages that a party provides via the DS is exhausted (see Section 17.4).
That was part of my question. When there were key packages in the leaves, you theoretically could fork the group from a given point in time and reuse the current tree, but I wasn't sure if that was a proper solution or not. Sounds like it isn't, but either way, I think there should be clarification in the document about this. Let me write up a quick PR on it.
NAME opened URL Would like your feedback there. We have conflicting wording here as well. What threw me off in the first place was the line indicating that LeafNodes should be the same in the sub-group, and if we are fetching new key packages they will not be the same.
NAME Konrad, could you safely derive new keys for the members of the subgroup instead? If so, that would be an enormous increase in efficiency and (especially in the federated case) make group creation timing consistent enough to do dynamically.
NAME Nice idea! But it's tricky, since we're dealing with public key pairs that the creator of the new group doesn't know the private keys for. If we were only using HPKE schemes that support updateable public key encryption, the creator of the new group could indeed update the key in the existing group and send the "delta" encrypted to each corresponding member (which would still represent an overhead, but we might be able to optimize this somehow). This is similar to the RTreeKEM mechanism that NAME et al. proposed at some point. To go all the way here, we'd have to do the same for the public key material in the credentials as well, which is not something we had considered before. It would be a new primitive, something like an updateable signature scheme? Probably something to consider for HPKE/MLS 2.0. We could also allow the re-use of LeadNodes for this particular case. It would be the simpler solution and I don't think it would be catastrophic, since we have to assume that LeafNode (or KeyPackage) key material is re-used in the "last resort" scenario anyway, but this would definitely be something we should discuss on the mailing list, as it would break a pretty basic assumption we've made until now.
Just catching up on this. NAME nice catch here. It seems like we have two options here: Require new KeyPackages (as in ) Invent some way to make a new group based on LeafNodes alone I probably agree that we should do (1) in the short term, but (2) also has some appeal -- it doesn't feel right to require new KeyPackages for folks you're already in touch with -- despite it probably being more spec text. So here's a sketch of (2) in the interest of completeness. Suppose we defined a message of the following form: ... which would pretty closely parallel to Commit. You would send it in MLSMessage and require a in MLSMessageAuth. Where Commit instructs the recipient to create a new epoch from the current one within the same group, Branch would instruct the recipient to create a new epoch that is the first epoch of a new group. In both cases, you would form an epoch / GroupContext as specified, then use the to ratchet into the new epoch. I'm pretty sure that would work and have the expected security properties, though obviously formal analysis would be appreciated. The main awkardness that occurs to me is that any members joining after the branch would have to provide new key packages. And obviously, it would require a fair bit more spec text. All that said, though, the exercise of working all that out has increased my impression that we should do (1) for now. If we want something like (2) later, it can be done pretty cleanly in an extension.
Fixed by
Well put!

The "version" and "ciphersuite" values in the Welcome MUST be the same as those used by the old group. <del> Each LeafNode in the new group's tree MUST be a leaf in the old group's tree at the epoch indicated in the PreSharedKey. </del> <ins> Each LeafNode in a new subgroup MUST match some LeafNode in the original group.  In this context, a pair of LeafNodes is said to "match" if the identifiers presented by their respective credentials are considered equivalent by the application. </ins> In addition, to avoid key re-use, the "psk_nonce" included in the "PreSharedKeyID" object MUST be a randomly sampled nonce of length


Question:
I drew this to help explain the relevant patch in MLSpp to someone, and thought it might be helpful to other folks.
Excellent! This is a very nice addition to explain the design introduced .

The following structure is used to fully describe the data transmitted in plaintexts or ciphertexts. <ins> The following figure illustrates how the various structures described in this section relate to each other, and the high-level operations used to produce and consume them: </ins> 7.1. MLSMessageContent is authenticated using the MLSMessageAuth


Question:
I'm not sure if this is actually necessary, but there usually seem to be issues in Merkle trees when the leaves are not explicitly distinct from the parents.
I would find it nicer to describe things with a like in other parts of the protocol: It is equivalent to your modification. Stating things that way ensures there is no ambiguity: TreeHashInput is parseable, which is I think the property you want to ensure with this PR.
Use enum instead and then ready for merge.
Interim 2022-05-26: Nicer with an NAME to update to , then merge
When computing the tree hash, should there be an indicator bit for whether the value being hashed represents a leaf or parent node?
This doesn't seem like a terrible idea. You could either add a field to LeafNodeHashInput and ParentNodeHashInput, or define an enum and do the same, or make a . For example:

cryptographic state of the group, this section defines a scheme for generating a hash value (called the "tree hash") that represents the contents of the group's ratchet tree and the members' leaf nodes. <del> The tree hash of a tree is the tree hash of its root node, which we define recursively, starting with the leaves. The tree hash of a leaf node is the hash of leaf's "LeafNodeHashInput" object which might include a "LeafNode" object depending on whether or not it is blank. Now the tree hash of any non-leaf node is recursively defined to be the hash of its "ParentNodeHashInput".  This includes an optional "ParentNode" object depending on whether the node is blank or not. The "left_hash" and "right_hash" fields hold the tree hashes of the node's left and right children, respectively. </del> <ins> The tree hash of an individual node is the hash of the node's "TreeHashInput" object, which may contain either a "LeafNodeHashInput" or a "ParentNodeHashInput" depending on the type of node.  "LeafNodeHashInput" objects contain the "leaf_index" and the "LeafNode" (if any).  "ParentNodeHashInput" objects contain the "ParentNode" (if any) and the tree hash of the node's left and right children. The tree hash of an entire tree corresponds to the tree hash of the root node, which is computed recursively by starting at the leaf nodes and building up. </ins> 8.8.


Question:
Also covers the PSK part of
Interim 2022-05-26: Ready to go once merge conflicts are resolved.
The collection of structs could be less chatty. Suggest PreSharedKeys is only used once, can be inlined in GroupSecrets Use GroupContext for the internals of GroupInfo
Note: was fixed in In , we did: So only the following remain: Use GroupContext for the internals of GroupInfo
currently described at time of use. In the "Pre-Shared Keys" section, we should just specify that it must always be a fresh value of length The ReInit provisions require , but those for branching do not. Presumably these should be the same? Currently, the processing rules for PreSharedKey proposal says “MUST be external”. This seems overly restrictive; for example, it rules out the scenario in Figure 8. Suggest “MUST NOT be branch/reinit” instead.

input to the key schedule, the Commit and GroupSecrets objects MUST indicate the same set of PSKs, in the same order. <ins> Each time a client injects a PSK into a group, the "psk_nonce" of its PreSharedKeyID MUST be set to a fresh random value of length "KDF.Nh", where "KDF" is the KDF for the ciphersuite of the group into which the PSK is being injected.  This ensures that even when a PSK is used multiple times, the value used as an input into the key schedule is different each time. </ins> On receiving a Commit with a "PreSharedKey" proposal or a GroupSecrets object with the "psks" field set, the receiving Client includes them in the key schedule in the order listed in the Commit,


Question:
Also covers the PSK part of
Interim 2022-05-26: Ready to go once merge conflicts are resolved.
The collection of structs could be less chatty. Suggest PreSharedKeys is only used once, can be inlined in GroupSecrets Use GroupContext for the internals of GroupInfo
Note: was fixed in In , we did: So only the following remain: Use GroupContext for the internals of GroupInfo
currently described at time of use. In the "Pre-Shared Keys" section, we should just specify that it must always be a fresh value of length The ReInit provisions require , but those for branching do not. Presumably these should be the same? Currently, the processing rules for PreSharedKey proposal says “MUST be external”. This seems overly restrictive; for example, it rules out the scenario in Figure 8. Suggest “MUST NOT be branch/reinit” instead.

Welcome message MUST be the same as the corresponding fields in the ReInit proposal. <del> The "epoch" in the Welcome message MUST be 1 </del> The Welcome MUST specify a PreSharedKey of type "resumption" with usage "reinit".  The "group_id" must match the old group, and the "epoch" must indicate the epoch after the Commit covering the ReInit. <del> The "psk_nonce" included in the "PreSharedKeyID" of the resumption PSK MUST be a randomly sampled nonce of length "KDF.Nh", for the KDF defined by the new group's ciphersuite. </del> Note that these three steps may be done by the same group member or different members.  For example, if a group member sends a commit with an inline ReInit proposal (steps 1 and 2), but then goes


Question:
Also covers the PSK part of
Interim 2022-05-26: Ready to go once merge conflicts are resolved.
The collection of structs could be less chatty. Suggest PreSharedKeys is only used once, can be inlined in GroupSecrets Use GroupContext for the internals of GroupInfo
Note: was fixed in In , we did: So only the following remain: Use GroupContext for the internals of GroupInfo
currently described at time of use. In the "Pre-Shared Keys" section, we should just specify that it must always be a fresh value of length The ReInit provisions require , but those for branching do not. Presumably these should be the same? Currently, the processing rules for PreSharedKey proposal says “MUST be external”. This seems overly restrictive; for example, it rules out the scenario in Figure 8. Suggest “MUST NOT be branch/reinit” instead.

The "version" and "ciphersuite" values in the Welcome MUST be the same as those used by the old group. <ins> The "epoch" in the Welcome message MUST be 1. </ins> Each LeafNode in a new subgroup MUST match some LeafNode in the original group.  In this context, a pair of LeafNodes is said to "match" if the identifiers presented by their respective credentials are considered equivalent by the application. <del> In addition, to avoid key re-use, the "psk_nonce" included in the "PreSharedKeyID" object MUST be a randomly sampled nonce of length "KDF.Nh". </del> Resumption PSKs with usage "branch" MUST NOT be used in other contexts.  A PreSharedKey proposal with type "resumption" and usage "branch" MUST be considered invalid.


Question:
Also covers the PSK part of
Interim 2022-05-26: Ready to go once merge conflicts are resolved.
The collection of structs could be less chatty. Suggest PreSharedKeys is only used once, can be inlined in GroupSecrets Use GroupContext for the internals of GroupInfo
Note: was fixed in In , we did: So only the following remain: Use GroupContext for the internals of GroupInfo
currently described at time of use. In the "Pre-Shared Keys" section, we should just specify that it must always be a fresh value of length The ReInit provisions require , but those for branching do not. Presumably these should be the same? Currently, the processing rules for PreSharedKey proposal says “MUST be external”. This seems overly restrictive; for example, it rules out the scenario in Figure 8. Suggest “MUST NOT be branch/reinit” instead.

A PreSharedKey proposal is invalid if any of the following is true: The "psktype" in the PreSharedKeyID struct is not set to <del> "external". </del> <ins> "resumption" and the "usage" is "reinit" or "branch". </ins> The "psk_nonce" is not of length "KDF.Nh".


Question:
The enum was missing the limit. The wasn't using a TLS environment.
NAME shouldn't ResumptionPSKUsage include (255)? enum { reserved(0), application(1), reinit(2), branch(3), (255) } ResumptionPSKUsage;
Fixed in
Seems reasonable to me.

A GroupContextExtensions proposal is used to update the list of extensions in the GroupContext for the group. <del> "struct { Extension extensions<V>; } GroupContextExtensions; " </del> A GroupContextExtensions proposal is invalid if it includes a "required_capabilities" extension and some members of the group do not support some of the required capabilities (including those added