15 KiB
MSC4268: Sharing room keys for past messages
In Matrix, rooms can be configured via the
m.room.history_visibility
state event such that previously-sent messages can be visible to users that
join the room. However, this is ineffective in encrypted rooms, where new
joiners will lack the keys necessary to decrypt historical messages.
This proposal defines a mechanism by which existing room members can share the decryption keys with new members, for example when inviting them, thus giving the new members access to historical messages.
A previous proposal,
MSC3061 aimed
to solve a similar problem; however, the mechanism used did not scale well. In
addition, the implementation in matrix-js-sdk was subject to a security
vulnerability
which this proposal addresses.
Proposal
Room key bundle format
When Alice is about to invite Bob to a room, she first assembles a "room key
bundle" containing all of the room keys for megolm sessions that she believes
future members of the room should have access to. Specifically, those are the
megolm sessions associated with that room which were marked with
shared_history: see below.
The keys are assembled into a JSON object with the following structure:
{
"room_keys": [
{
"algorithm": "m.megolm.v1.aes-sha2",
"room_id": "!Cuyf34gef24t:localhost",
"sender_claimed_keys": { "ed25519": "aj40p+aw64yPIdsxoog8jhPu9i7l7NcFRecuOQblE3Y" },
"sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
"session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ",
"session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8Llf..."
},
// ... etc
],
"withheld": [
{
"algorithm": "m.megolm.v1.aes-sha2",
"code": "m.history_not_shared",
"reason": "History not shared",
"room_id": "!Cuyf34gef24t:localhost",
"sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
"session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ"
}
]
}
The properties in the object are defined as:
-
room_keys: an array of objects each with the following fields fromExportedSessionData:algorithmroom_idsender_claimed_keyssender_keysession_idsession_key
forwarding_curve_key_chain is omitted since it is useless in this case.
The shared_history flag defined below is omitted (it is true by
implication).
withheld: an array of objects with the same format as the content of anm.room_key.withheldmessage, usually with codem.history_not_shared(see below) to indicate that the recipient isn't allowed to receive the key.
A single session MUST NOT appear in both the room_keys and withheld sections.
The JSON object is then encrypted using the same algorithm as encrypted
attachments
(i.e., with AES256-CTR), and uploaded with POST /_matrix/media/v3/upload.
The details of this key bundle are then shared with Bob, as below.
m.room_key_bundle to-device message
Having uploaded the encrypted key bundle, Alice must share the details with each of Bob's devices.
She first ensures she has an up-to-date list of his devices (performing a
/keys/query
request if necessary. She then sends a to-device message to each of his devices
which are correctly signed by his cross-signing keys.
A new to-device message type is defined, m.room_key_bundle, which MUST be
encrypted using
Olm.
The plaintext content of such a message should be:
{
"type": "m.room_key_bundle",
"content": {
"room_id": "!Cuyf34gef24t:localhost",
"file": {
"v": "v2",
"url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe",
"key": {
"alg": "A256CTR",
"ext": true,
"k": "aWF6-32KGYaC3A_FEUCk1Bt0JA37zP0wrStgmdCaW-0",
"key_ops": ["encrypt","decrypt"],
"kty": "oct"
},
"iv": "w+sE15fzSc0AAAAAAAAAAA",
"hashes": {
"sha256": "fdSLu/YkRx3Wyh3KQabP3rd6+SFiKg5lsJZQHtkSAYA"
}
}
},
"sender": "@alice:example.com",
"recipient": "@bob:example.org",
"recipient_keys": { "ed25519": "<bob_ed25519_key>" }
"keys": { "ed25519": "<alice_ed25519_key>" },
"sender_device_keys": { ... }
}
The properties within the content are defined as:
-
room_id: the room to which the keys in the key bundle relate. (This is required so that Bob can download the key bundle at the right time.) -
file:EncryptedFilefrom the encrypted attachment format.
sender, recipient, recipient_keys and keys are the normal fields
required by Olm-encrypted messages.
sender_device_keys are the sender's device keys, as defined by
MSC4147, which
are required for m.room_key_bundle messages. The sender MUST include
them, and recipients SHOULD ignore m.room_key_bundle messages which omit
them.
shared_history property in m.room_key messages
Suppose Alice and Bob are participating in an encrypted room, and Bob now wishes to invite Charlie to join the chat. If the history visibility settings allow, Bob can share the message decryption keys for previously sent messages with Charlie. However, it is dangerous for Bob to take the server's word for the history visibility setting: a malicious server admin collaborating with Charlie could tell Bob that the history visibility was open when in fact it was restricted. In addition, the history visibility in a given room may have been changed over time and it can be difficult for clients to estalish which setting was in force for a particular Megolm session.
To counter this, we add a shared_history property to
m.room_key
messages, indicating that the creator of that Megolm session understands and
agrees that the session keys may be shared with newly-invited users in
future. For example:
{
"type": "m.room_key",
"content": {
"algorithm": "m.megolm.v1.aes-sha2",
"room_id": "!room_id",
"session_id": "session_id",
"session_key": "session_key",
"shared_history": true
}
}
In other words: when Alice wants to send a message in the room she shares with
Bob, she first checks the history_visibility. If it is shared or
world_readable, then when she sends the Megolm keys to Bob, she sets
shared_history to true.
Clients SHOULD show a visual indication to users that their encrypted messages may be shared with future room members in this way.
If the history visibility changes in a way that would affect the
shared_history flag (i.e., it changes from joined or invited to shared
or world_readable, or vice versa), then clients MUST rotate their outbound
megolm session before sending more messages.
In addition, a shared_history property is added to the BackedUpSessionData
type
in key backups (that is, the plaintext object that gets encrypted into the
session_data field) and the ExportedSessionData
type. In
both cases, the new property is set to true if the session was shared with us
with shared_history: true, and false otherwise.
For example:
{
"algorithm": "m.megolm.v1.aes-sha2",
"forwarding_curve25519_key_chain": [
"hPQNcabIABgGnx3/ACv/jmMmiQHoeFfuLB17tzWp6Hw"
],
"sender_claimed_keys": {
"ed25519": "aj40p+aw64yPIdsxoog8jhPu9i7l7NcFRecuOQblE3Y"
},
"sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
"session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8Llf...",
"shared_history": true
}
In all cases, an absent or non-boolean shared_history property is treated the same as
shared_history: false.
New "withheld" code
The spec currently
defines a
number of "withheld" codes which are used to indicate that a client is
deliberately not sharing a megolm session key with another. Normally these
codes are used in m.room_key.withheld to-device events; as the text above
specifies, we will now also use them in the withheld section of the room key bundle.
This MSC proposes the addition of a new withheld code, m.history_not_shared,
which is used specifically to indicate that the megolm session in question does not
have the shared_history flag set (which means that the creator of that
session believed that the room history visibility did not allow new members to
access history).
-
Aside: the spec currently contains a definition for a
withheldcodem.unauthorised. However, its semantics are unclear: the spec defines it as meaning "the user/device is not allowed to have the key", but is unclear about why this might happen. (Arguably,m.blacklistedandm.unverifiedare also cases of "the user/device is not allowed to have the key".)In practice, modern Element clients (including Element Web and the classic mobile clients, since the port to the Rust crypto stack), do not send this withheld code at all. Further, the example given in the spec, "the user/device was not in the room when the original message was sent", is somewhat similar to this usecase.
It is therefore somewhat tempting to repurpose
m.unauthorisedto suit this usecase. However,m.unauthorisedhas been used for other purposes in the past (for example, Element Android Classic used to use it as a general-purpose refusal to respond to key requests from other users), and we have little insight as to howm.unauthorisedmight be used in non-Element clients.In short, a new code is likely to cause less confusion than repurposing
m.unauthorised,
Actions as a receiving client
When Bob's client receives an m.room_key_bundle event from Alice, there are two possibilities:
-
If Bob has recently accepted an invite to the room from Alice, the client should immediately download the key bundle and start processing it. Note, however, that this process must be resilient to Bob's client being restarted before the download/import completes.
TODO: what does "recently" mean?
-
Otherwise, Bob's client should store the details of the key bundle but not download it immediately. If he later accepts an invite to the room from Alice, his client downloads and processes the bundle at that point.
Delaying the download in this way avoids a potential DoS vector in which an attacker can cause the victim to download a lot of useless data.
Once Bob has downloaded the key bundle, the sessions are imported as they would be when importing a key export; however:
-
Only keys for the relevant room should be imported.
-
Bob's client should remember who sent the keys (Alice, in this case), and MUST show that information to the user, since he has only that user's word for the authenticity of those sessions.
TODO: tell the sender we have finished with the bundle, so they can delete it?
Potential issues
Alternatives
Security considerations
-
The proposed mechanism allows clients to share the decryption keys for significant amounts of encrypted content. Sharing historical keys in this way represents a significantly greater security risk than sharing keys for future messages on an ad-hoc basis, as when sending
m.room_keymessages.It is therefore crucial that the inviting client take careful measures to ensure that the recipient devices genuinely belong to the intended recipient, rather than having been injected by an intruder.
For example, the recipient must cross-sign his devices, and the sender must ensure that the devices are correctly signed. Further, the sender should keep records of cross-signing keys seen for each user, and if a change is observed, consider this a red flag suggesting that the account may be compromised and confirm with the user.
-
Recipients must be mindful that there is no authoritative evidence of the sender of messages decrypted using a room key bundle: a malicious (or buggy) inviter working in cahoots with a homeserver administrator could make it appear as though events sent by one user were in fact sent by another.
Ultimately, the recipient of a key bundle is taking the world of the sender of that key bundle as to the actual owner of each megolm session. This is an inevitable consequence of the deniability property of encrypted messaging.
Recipient clients should make this constraint obvious to the user, for example by showing the affected messages with a label "Alice shared this message".
-
Recipients should be mindful of the potential of denial-of-service (DoS) and cache-poisoning attacks from malicious senders.
In particular, a malicious sender could try to prompt a recipient to download significant amounts of data from the media store by sending
m.room_key_bundlemessages pointing to large media files.Further, a malicious sender might attempt to make the recipient believe that a megolm session belonged to Alice, whereas it actually belonged to Charlie. Even if the recipient later receives a key bundle from an honest user, they may now have difficulty deciding which user was correct.
Both problems can be mitigated by only accepting key bundles when accepting an invite from that user.
Unstable prefix
Until this MSC is accepted, the following identifiers should be used:
-
io.element.msc4268.room_key_bundleinstead ofm.room_key_bundlefor the to-device message containing details of the key bundle. -
org.matrix.msc3061.shared_historyinstead ofshared_historyfor the property inBackedUpSessionDataandExportedSessionDataindicating that the key can be shared with new members. -
io.element.msc4268.history_not_sharedinstead ofm.history_not_sharedas the withheld code for sessions which are not marked asshared_history.
Dependencies
This MSC depends on MSC4147, which has recently been accepted into the spec.