Skip to content
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

Closed
wants to merge 7 commits into from
Closed

Conversation

bifurcation
Copy link
Collaborator

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

Copy link
Member

@beurdouche beurdouche left a 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.

opaque content[MLSPlaintext.length];
uint8 signature[MLSInnerPlaintext.sig_len];
uint16 sig_len;
ContentType type;
Copy link
Member

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


struct {
opaque content[MLSPlaintext.length];
uint8 signature[MLSInnerPlaintext.sig_len];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sig_len is undefined.

struct {
opaque content[MLSPlaintext.length];
uint8 signature[MLSInnerPlaintext.sig_len];
uint16 sig_len;
Copy link
Member

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>;
Copy link
Member

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 ?

Copy link
Member

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.

Copy link
Contributor

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;
Copy link
Member

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.

Copy link
Member

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...

Copy link
Contributor

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
Copy link
Member

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.

@raphaelrobert
Copy link
Member

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;
Copy link
Contributor

@kkohbrok kkohbrok Feb 27, 2019

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?

Copy link
Collaborator Author

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;
Copy link
Contributor

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?

Copy link
Member

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;
Copy link
Contributor

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 :-)

Copy link
Member

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.

Copy link
Contributor

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.

Copy link
Member

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 ? :)

Copy link
Contributor

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;
Copy link
Member

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.

@bifurcation
Copy link
Collaborator Author

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 |
~~~~~
Copy link
Contributor

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.

@bifurcation bifurcation mentioned this pull request Feb 28, 2019
@bifurcation
Copy link
Collaborator Author

Closing in favor of #131

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

Use common framing
4 participants