mlswg / mls-protocol Goto Github PK
View Code? Open in Web Editor NEWMLS protocol
Home Page: https://messaginglayersecurity.rocks
License: Other
MLS protocol
Home Page: https://messaginglayersecurity.rocks
License: Other
Sofรญa noted "It is also unclear at some points the notation used for the Diffie-Hellman or
Elliptic Curve parameters. Related to this, is unsure if this protocol takes
into account the cofactor issues."
If a group member can signal what its latest epoch is (e.g., by ACKing a handshake message), then other members know that they can safely delete any keys older than that epoch, at least for that sender.
Currently, the tree only ever grows:
We should enable reclaiming of blank leaves and automatically reduce the size of the tree to the minimum necessary size.
We have some text about how Handshake messages need to be sequenced, but clients will need some retry logic to deal with cases where there's a gap between the premise for their action and the state of the group when the action can be taken. We should describe the logic a client should follow for retrying with each of the available operations. For example, Update can just be retried, but Add and Remove should be checked for duplication before retrying.
I think a bunch of these are probably relics of previous versions. For reference, the page numbers listed are from the PDF version of draft 3 of the protocol:
1 - p6: GroupInit
is mentioned, but it is not defined anywhere in the specification. Perhaps Init
is intended.
2 - p19: A transcript
field is mentioned, but only transcript_hash
is present. Also it's unclear which Hash
function is supposed to be used here. It can't be ciphersuite-dependent, since it has to be the same for every Participant.
3 - p22: update_secret
is used in a diagram, but never defined. I think this is important, and it's mentioned elsewhere.
4 - p24: finished_mac
is mentioned, but never defined. Is it supposed to say confirmation
?
5 - p25: The underlying Hash that HMAC uses can't be defined by anyone's ciphersuite. This is group state, and everyone has to agree on this.
6 - p28: Mentioned "update secret" again without explanation.
7 - p29: "identity tree" is mentioned, but I can't tell what that's referring to. Also "update secret" is here.
8 - p35: MLSPlaintext
is mentioned, but never defined. Perhaps ApplicationPlaintext
is intended.
These keys are Client one-time use keys, so "UserInitKey" seems like a strange name.
From @raphaelrobert ... This would be a useful terminology to describe a client which shares its cryptographic material across multiple device.
To be discussed ?
There is a nifty advantage of using Mike Hamburg's STROBE for symmetric cryptography:
Applications using WASM virtual machines should improve performance by using native code for cryptography. STROBE works well for this because all actual processing is handled by one call that processes an arbitrary amount of data. You always require multiple STROBE calls per operation in practice of course, but only O(1) cals. If you wrapped say a ChaCha or Keccak permutation directly then youโd have O(data_size) calls. In short, STROBE simplifies the WASM call boundary dramatically without much performance penalty.
In MLS, you might favor adding ChaCha into your WASM call boundary anyways because STROBE lacks any round count configuration. I wanted to mention this however since messengers do commonly get implemented in VMs.
We should ensure that there is adequate anti-replay protection for both Handshake and Application messages. (In particular, we should define what anti-replay issues are a concern.) Could be related to #101 if we want to solve it at that framing layer.
We could use the sender_generation field of the MLS message to signal what was the global app generation of the sender. Functionally, this allows receivers to know if they received all previous application messages from the sender. In the current design, if S sent M0, M1, M2 you can know if you missed M1 when receiving M2, but you cannot know if you missed M2. There are many ways of doing this: global sender counter, counter between two operations of the sender...
Here's the Weekly Digest for mlswg/mls-protocol:
Last week 4 issues were created.
Of these, 1 issues have been closed and 3 issues are still open.
๐ #153 Common framing, consolidated, by bifurcation
๐ #152 Make epochs unpredictable, by bifurcation
๐ #150 Allow direct initialization, by bifurcation
โค๏ธ #151 A couple of minor fixes, by bifurcation
๐ #152 Make epochs unpredictable, by bifurcation
It received 4 comments.
Last week, 9 pull requests were created, updated or merged.
Last week, 8 pull requests were updated.
๐ #153 Common framing, consolidated, by bifurcation
๐ #152 Make epochs unpredictable, by bifurcation
๐ #148 Added that users SHOULD verify pubkeys in an Update, by rozbb
๐ #147 Added public_key_index to Add message, by rozbb
๐ #146 Tree based app keyschedule, by psyoptix
๐ #143 Fixes to tree manipulation in Remove, by rozbb
๐ #134 Tree Hash, by bifurcation
๐ #131 Common Framing, by beurdouche
Last week, 1 pull request was merged.
๐ #151 A couple of minor fixes, by bifurcation
Last week there were 4 commits.
๐ ๏ธ Merge pull request #151 from mlswg/minor-fixes A couple of minor fixes by bifurcation
๐ ๏ธ Fix error noted by @rozbb by bifurcation
๐ ๏ธ Make welcome/add diagram clearer by bifurcation
๐ ๏ธ Open issue cleanup by bifurcation
Last week there was 1 contributor.
๐ค bifurcation
Last week there was 1 stargazer.
โญ Mromson
You are the star! ๐
Last week there were no releases.
That's all for last week, please ๐ Watch and โญ Star the repository mlswg/mls-protocol to receive next weekly updates. ๐
You can also view all Weekly Digests by clicking here.
Your Weekly Digest bot. ๐
This allows combo operations -- multiple adds, moves, resyncs -- without having to worry about getting interrupted mid-operation.
Currently, the whole GroupState object is folded into the key schedule, via the DeriveSecret
function. Could this be simplified? For example:
Hash the GroupState object once, and use it for all DeriveSecret
calls, as opposed to passing it by value each time
Hash in the roster and tree as a commitment (cf #87) rather than by value
Other suggestions welcome.
We need some story for how to evolve versions over time. How and where is version support indicated? How can a group transition from one version to another?
This text is wrong:
key = HKDF-Expand(Secret, ECIESLabel("key"), Length)
nonce = HKDF-Expand(Secret, ECIESLabel("nonce"), Length
This was resolved in #80, but recording it in this issue for posterity. A Welcome message carries the init_secret
value for the group, and so it needs to be encrypted for the new joiner.
The struct proposed in the revised draft wraps the information in the Welcome in an ECIES ciphertext, and adds enough information to enable the joiner to identify the private key it needs to decrypt the Welcome message.
struct {
opaque group_id<0..255>;
uint32 epoch;
optional<Credential> roster<1..2^32-1>;
optional<PublicKey> tree<1..2^32-1>;
opaque transcript_hash<0..255>;
opaque init_secret<0..255>;
} WelcomeInfo;
struct {
opaque user_init_key_id<0..255>;
CipherSuite cipher_suite;
ECIESCiphertext encrypted_welcome_info;
} Welcome;
GroupOperation.confirmation =
HMAC(confirmation_key, GroupState.transcript\_hash)
Security considerations should provide guidance about how often to rotate, last resort, etc.
We have several types of top-level objects right now:
We should have a common framing layer that describes the object type (and optionally provides encryption), to prevent confusion among these types. This would be analogous to the TLS record layer
I can't wait to see the implementations.
In the current draft, creating a group is done with n
Add messages, which just place the members of the group at the leaves of the tree. This means that newly-created trees have some "warm up" time during which early operations are roughly linear size, gradually converging to log size.
This state of affairs is unavoidable if we are not going to have double joins. However, it might be tolerable to have double joins, so long as (1) they only happen at group creation time and (2) they resolve relatively quickly.
For example, you might create a special Init
message, distinct from Add, which initializes a tree with the members at the leaves, but also some intermediate nodes filled in by the creator (and thus double-joined). These nodes would be overwritten as members update, but the double-joins would only be fully resolved once all members update (or 1/2^{k}
of the members if k
lower layers are not double-joined).
There are at least two bugs in the message protection section that make it unimplementable:
It calls for Derive-Secret(., "app sender", [sender])
, but the third argument to Derive-Secret
as defined is a GroupState
, not an octet string.
It refers to a function HKDF-Expand-Label
, which is defined in TLS, but not here.
ISTM the simplest way to fix these problems would to define HKDF-Expand-Label
and use it (1) as the basis for Derive-Secret
, (2) for deriving sender root secrets, and (3) for deriving keys and nonces.
In current proposals, it is possible for the delivery service to distinguish handshake and application messages. This enables the DS to lock in a compromise by forbidding key updates while allowing application messaging to continue. We should make it harder to distinguish handshake and application messages, e.g., by encrypting the content type.
We inconsistently use the backtick, the underscore or nothing for messages fields or specific operations in the draft. This needs a fix, currently the opinion is to use the TLS style (aka remove everything)
Sometimes a client falls out of sync with the rest of the group, and it is necessary to re-initialize it. What protocol mechanisms are necessary to make this possible?
Perhaps it would be useful to look at the state of the art with regard to how desync occurs and how it repaired.
I think there's something missing from the following sentence in s5.1:
A _subtree_ of a tree is the tree given by the descendants of any
node, the _head_ of the subtree The _size_ of a tree or subtree is
the number of leaf nodes it contains.
We talked about quite a number of different ways to do deletion in *ART groups. Give at least a high-level overview of the main ones, and mention their pros and cons. These include:
In early discussions around MLS, people expressed interest in specifying how to do ACKs / NACKs on messages. Questions to address:
At least 2 existing implementations of MLS have some sort of curve-specific DH public key type. This is to ensure type safety and reduce the likelihood of the programmer accidentally using one point value for a different curve. For me, this is
enum DhPoint {
X25519([u8; 32]),
P256([u8; 32]),
}
The issue I run into is that I cannot unambiguously deserialize DHPublicKey
s into a DHPoint
. Recall the definition opaque DHPublicKey<1..2^16-1>
. The parse of an unmarked sequence of bytes is dependent on ambient state, namely the CipherSuite
of the current session. Notably, the value of this CipherSuite
is not repeated in any top-level messages. This means that a given Handshake
message will have at least two correct interpretations unless context is specified.
This is not a massive issue, but one could imagine a case where a participant receives a message from another participant who somehow disagrees on the current CipherSuite
. The receiver will parse all the messages correctly, check the signatures, and throw an invalid signature error. If we were to mark the type of the DHPublicKey
, though, the receiver would parse the values and check that they agree with the current CipherSuite
. This check will fail, and it will throw a decoherence error, which is more appropriate and informative.
DHPublicKey
is not the only data type for which this ambiguity occurs. I propose that we change at least some opaque
datatypes to enums. Currently, the not-really-bytes opaque types are
DHPublicKey
SignaturePublicKey
ECIES::ciphertext
UserInitKey::signature
Handshake::signature
ApplicationPlaintext::signature
Application::encrypted_content
My own implementation makes distinctions between different signature types, ECDSA pubkey types, and ECDH pubkey types. Encrypted content is treated as a binary blob.
We have a long-standing OPEN ISSUE on creating a group with a single Init message instead of N Add messages, which would change the work of initialization from O(N log N) to O(N). I would propose roughly the following:
struct {
Welcome creator_info<0..2^32-1>; // Welcome from one-member group to each joiner
UserInitKey members<0..2^32-1>; // Info to populate at the leaves
DirectPath creator_update; // Update from the creator to warm the tree
} Init;
Then the processing would be:
Currently, clients are expected to cache the entire roster and tree (of size n
and 2n-1
, respectively), and the Welcome
message provides these to the new joiner by value. In principle it's possible to create a scheme where:
For example, if the roster were committed as a Merkle tree root, an Add message could come with (1) an inclusion proof for the signer's public key, relative to the prior tree root; and (2) a consistency proof between the prior tree root and the updated root.
This would open up a spectrum of options for how much state the clients have to cache (note that the client has to cache its direct path in any case, to be able to decrypt TreeKEM encryptions). The current draft would be on one end, with clients storing everything. The other end would have clients store nothing and handshake messages just carrying new entropy and updating commitments.
As an example of an intermediate state, if you assume that Updates are more common than Adds/Removes, you could require each client to cache its own copath. Then Add/Remove/Update messages would have to provide enough information about the tree to allow the other clients to update their copaths (in addition to updating the commitments).
We need to discuss and agree on consistent definitions and terminology across documents.
I'll implement the changes for -02
It has been noted that we don't discuss enough he influence of clients refusing to update in this document. The architecture document briefly explain why we minimally should evict clients that refuse to update to preserve FS or PCS. I think we actually enforce a MUST and provide a minimal recommendation.
### Membership and offline members
Because Forward Secrecy (FS) and Post-Compromise Security (PCS)
rely on the deletion and replacement of keying material,
any client which is persistently offline
may still be holding old keying material and thus be a threat
to both FS and PCS if it is later compromised.
MLS does not inherently defend against this problem, but
MLS-using systems can enforce some mechanism for doing
so. Typically this will consist of evicting clients which
are idle for too long, thus containing the threat of
compromise. The precise details of such mechanisms are
a matter of local policy and beyond the scope of this document.
The definition of MLSCiphertextContent
is incorrect as it relies on MLSInnerPlaintext.sig_len
. Should we make the signature
opaque anyway ?
Consumers should not assume that UIK IDs are unique across users.
The PR from @kkohbrok introducing a KDF was merged before I had a chance to review it. There are a number of problems with it:
private_key = KDF(...)
, since KDFs produce octet strings, not private keys. We need Derive-Key-Pair()
, not just Derive-Public-Key()
.I think what you want is actually as follows:
path_secret[n+1]
^
|
path_secret[n] --> node_secret --> node_key_pair
Or in prose:
node_secret = HKDF-Expand-Label(path_secret[n], "node", "", Hash.Length)
node_private, node_public = Derive-Key-Pair(node_secret)
path_secret[n+1] = HKDF-Expand-Label(path_secret[n], "path", "", Hash.Length)
Right now, nonces used to encrypt application messages are derived off the key schedule. Since the keys are effectively single-use, this could probably be done more simply, e.g., by using the generation number as the nonce.
As proposed on the list, we should re-use the encrypted-message framing for encrypted handshake messages. For better key separation, we might derive a separate set of keys for this purpose (the academics prefer this).
If we do key separation, we should have a separate sequence number space. In any case, a type field will be needed to distinguish.
In the current draft it is not known to the other group members when to discard the participant application secret from the previous epoch. My formal specification sends the 4 byte application message counter of the previous epoch within the first application message of each participant in the new epoch. Discuss and PR.
At the end of the London meetup we discussed a potential problem: malicious clients can send a bad copath (e.g. just random group elements), which will totally screw up the group state of anyone who processes the update. This was not considered a valid attack in ART, which does not consider malicious clients or DoS, but is more of a problem here. In particular, one can imagine a group with the convention "invite anybody, delete people who misbehave" (e.g. like many IRC channels).
There are a few ways to handle this:
Long-term inactive users undermine the FS and PCS properties of the protocol. Obviously, users can remove each other if they notice that a participant is inactive. We should consider whether we want to allow the server to do such a removal.
It has been pointed out that the text is not clear enough about the fact that once the Application secrets of the senders have been computed, the shared Application secret MUST be deleted. Make that clear.
We should change the name of this struct as it doesn't even contain the tree.
Here's the Weekly Digest for mlswg/mls-protocol:
Last week 5 issues were created.
Of these, 1 issues have been closed and 4 issues are still open.
๐ #173 Formatting: removed \_
where it was syntactically incorrect, by rozbb
๐ #172 DirectPathNode contains encrypted node secrets, not path secrets, by rozbb
๐ #171 Specify an Init message, by bifurcation
๐ #170 Confirmation and transcript improvements, by bifurcation
โค๏ธ #169 Notion of virtual client when secrets are shared accross devices, by beurdouche
Last week, 9 pull requests were created, updated or merged.
Last week, 3 pull requests were opened.
๐ #173 Formatting: removed \_
where it was syntactically incorrect, by rozbb
๐ #172 DirectPathNode contains encrypted node secrets, not path secrets, by rozbb
๐ #171 Specify an Init message, by bifurcation
Last week, 1 pull request was updated.
๐ #170 Confirmation and transcript improvements, by bifurcation
Last week, 5 pull requests were merged.
๐ #169 Notion of virtual client when secrets are shared accross devices, by beurdouche
๐ #167 Fix incorrect definition of update_secret, by beurdouche
๐ #166 GroupState is not a group state, renaming to GroupContext, by beurdouche
๐ #164 Rename UserInitKey to ClientInitKey, by beurdouche
๐ #163 Reorder blanking and update in the Remove operation, by beurdouche
Last week there were 8 commits.
๐ ๏ธ Merge pull request #164 from mlswg/beurdouche_clientinitkey Rename UserInitKey to ClientInitKey by bifurcation
๐ ๏ธ Merge branch 'master' into beurdouche_clientinitkey by bifurcation
๐ ๏ธ Merge pull request #163 from mlswg/beurdouche_remove Reorder blanking and update in the Remove operation by bifurcation
๐ ๏ธ Remove: truncate the tree on sending by beurdouche
๐ ๏ธ Fix merge conflict with master by beurdouche
๐ ๏ธ [GroupState is not a group state, renaming to GroupContext (#166) * GroupState is not a group state, renaming to GroupContext
Last week there were 2 contributors.
๐ค bifurcation
๐ค beurdouche
Last week there was 1 stargazer.
โญ eknoes
You are the star! ๐
Last week there were no releases.
That's all for last week, please ๐ Watch and โญ Star the repository mlswg/mls-protocol to receive next weekly updates. ๐
You can also view all Weekly Digests by clicking here.
Your Weekly Digest bot. ๐
Currently, in order for a new member to join a group, an existing member needs to add them to the group. Certain use cases work more naturally if a new joiner can initiate the add process. I think that broadly speaking we want a process here where someone outside the group can (1) request to be added and (2) send to the group, but cannot receive from the group until added.
Some things to consider here:
Anything that is revealed to a joiner that joins this way is effectively public. Should they be able to see, e.g., the roster?
How should these interactions relate to the key schedule? Assuming there's some AddRequest
message, should it be included into the transcript at Add time?
What happens if there are multiple parallel requests?
Please correct me if my understanding is wrong:
UserAdd(.->D)
and state.add(D)
initiated by user Z
. Isn't it UserAdd(.->Z)
and state.add(Z)
?Update(A)
message from server telling A and B to state.upd(D)
and Z to state.upd(A)
. Shouldn't all of them be state.upd(A)
, because A initiated the update?A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.