Code change:
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

Related discussion:
text: 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.