New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Introduce a common framing layer #120
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How to handle the application secret generation in this common framework is interesting, we can either always include it or having it only in the case of Application messages which will degrade the symmetry.
draft-ietf-mls-protocol.md
Outdated
opaque content[MLSPlaintext.length]; | ||
uint8 signature[MLSInnerPlaintext.sig_len]; | ||
uint16 sig_len; | ||
ContentType type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The signature must cover everything including the content type
draft-ietf-mls-protocol.md
Outdated
|
||
struct { | ||
opaque content[MLSPlaintext.length]; | ||
uint8 signature[MLSInnerPlaintext.sig_len]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sig_len
is undefined.
draft-ietf-mls-protocol.md
Outdated
struct { | ||
opaque content[MLSPlaintext.length]; | ||
uint8 signature[MLSInnerPlaintext.sig_len]; | ||
uint16 sig_len; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are gonna do this, I would prefer opaque signature<0..2^16-1>
otherwise if the ciphersuite* gives the length of the signature we don't need this.
case application: Application; | ||
} | ||
|
||
opaque signature<1..2^16-1>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am confused now, why is there a second signature here ?
In both cases there is a signature that covers the content of group_id
, epoch
, sender
and content type
no ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the contrary, the inner one is the one we use, you don't want to sign a large padding.
What is important is that the inner one must cover those fields as done the message protection section.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, then we're assuming that group_id
, sender
and epoch
are still part of the Handshake
struct? I was wondering if we could somehow avoid redundancy here.
opaque group_id<0..255>; | ||
uint32 epoch; | ||
uint32 sender; | ||
uint32 generation; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this is weakly-authenticated via the AEAD it should be in the MLSPlaintext header as well and covered by the signature (especially in the case of an Application message) to avoid forgery from an adversarial member who is not the sender.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is also worth noting that this field even if not technically needed in the case of a Handshake message might be able to use this to advertise a counter for application messages previously sent by the sender, hence allowing recipients to know if they received all app messages from the sender. We could limit that to the previous epoch or not... Maybe there is something there or not...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the generation field for exactly?
# Message Protection | ||
# Application Messages | ||
|
||
[[ XXX(RLB): It should be possible to simplify this section in light |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can do that when this PR is merged.
As a general rule, I think we should not prefix structs with "MLS" as it adds no semantic value |
handshake(1), | ||
application(2), | ||
(255) | ||
} ContentType; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would content ever be labelled invalid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea is that the ContentType is used to mark where the padding bytes (all zero) end, so it needs to be non-zero.
} | ||
|
||
opaque signature<1..2^16-1>; | ||
} MLSPlaintext; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need another epoch field, as well as another group_id field? Both should be implicitly verified upon successful decryption with the group secret from the epoch contained in MLSCiphertext, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not need to actually copy them twice indeed, only the outer one in Ciphertext
is needed, but they need to be covered by both the AEAD and the internal signature.
uint32 sender; | ||
uint32 generation; | ||
opaque cipertext<0..2^32-1>; | ||
} MLSCiphertext; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do we need the sender field for in MLSCiphertext? We might even get some sort of identity hiding if we leave it out :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was misunderstanding Richard's idea at first. I believe the idea is to encrypt the Plaintext
with different keys depending if it is Handshake
or Application
. In that scenario, you need the sender
and the generation
in the clear to know which key to pick in the case of an Application message. Note that @raphaelrobert and I are thinking on suggesting to encrypt these under the the group key to hide them if necessary, we'll discuss this at some point. Note that if this is the intent here, the ContentType must also be outside to know if this is an Application message or an Handshake message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need the sender, though? Both handshake and application keys derived from the groupkey should be sender-independent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which key do you pick when receiving an application message ? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused. The key for handshake encryption is derived from the group_key
, right? So why do we need to know the sender? We only need the group_id
and the epoch
to derive the key if I understand correctly.
opaque group_id<0..255>; | ||
uint32 epoch; | ||
uint32 sender; | ||
ContentType type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My initial interpretation was incorrect but, if I am correct now, above this should be removed.
I added some prose in the latest round of commits that should clarify what's going on here. I think that should address most of the comments. @raphaelrobert - regarding the "MLS" prefix, I'm inclined to keep it, because (1) it mirrors TLS, and (2) just having Plaintext and Ciphertext seems odd to me. |
| type | | ||
| | ||
* zero_padding | | ||
~~~~~ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do I understand correctly that the fields that represent information that is shared between the different structs are not actually part of the encoded data structure but rather are just meant to be pointers to the respective "outer" struct? If that is the case, I think we should stay true to the principle of having the structs represent what is actually part of the data structure that is encoded and not what is composed locally to compute signatures. Please do correct me if I'm wrong in my understanding of the spec here.
Closing in favor of #131 |
This PR introduces a common framing layer that is used to encapsulate both Handshake and Application messages, encrypted or unencrypted. The most significant impact is that in order to make signing logic common to both of these cases, the signature now covers only the message in question, as opposed to also covering the transcript.
Fixes #101