diff --git a/content/client-server-api/modules/end_to_end_encryption.md b/content/client-server-api/modules/end_to_end_encryption.md index 88a97bdd..66947c78 100644 --- a/content/client-server-api/modules/end_to_end_encryption.md +++ b/content/client-server-api/modules/end_to_end_encryption.md @@ -1547,6 +1547,143 @@ objects described as follows: {{% definition path="api/client-server/definitions/megolm_export_session_data" %}} +#### Sharing keys between users + +{{% added-in v="1.19" %}} + +When Alice invites Bob to an encrypted room, she might want Bob to have access +to messages that were previously sent in that room, subject to the [history +visibility](#room-history-visibility) setting of the room. + +Alice does this by constructing an [encrypted key +bundle](#construction-and-sharing-of-the-key-bundle) and sharing it with Bob +before inviting him. + +##### Shareable encryption sessions + +Only room keys which were marked as "shareable" by the creator of the +encryption session should be shared with new users. + +When Alice wants to send a message in the room she shares with Bob, she first +checks the [history visibility](#room-history-visibility) state of the +room. If it is `shared` or `world_readable`, then when she sends the Megolm +keys to Bob via an [`m.room_key`](#mroom_key) message, she sets +`shared_history` to `true`. + +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. + +Clients SHOULD show a visual indication to users that their encrypted messages +may be shared with future room members in this way. + +Recipients SHOULD keep a record of the `shared_history` flag for each +encryption session. The state of the flag is also saved in the +[`BackedUpSessionData`](#definition-backedupsessiondata) type in key backups, +and the [`ExportedSessionData`](#definition-exportedsessiondata) type in key +exports. + +A session is therefore considered as "shareable" if any of the following +conditions are satisfied: + + * The client created the session itself, when the `history_visibility` state + was set to `shared` or `world_readable`. + * The keys to the session were received from an [`m.room_key`](#mroom_key) + message with `shared_history` set to `true`. + * The keys to the session were loaded from key backup, and the + [`BackedUpSessionData`](#definition-backedupsessiondata) structure had + `shared_history` set to `true`. + * The keys to the session were loaded from a key export, and the + [`ExportedSessionData`](#definition-exportedsessiondata) structure had + `shared_history` set to `true`. + * The keys were received as part of a + [`RoomKeyBundle`](#definition-roomkeybundle). + +{{% boxes/note %}} +Tracking shareable sessions in this way prevents an attack where a malicious +homeserver can incorrectly flag history as shared, without telling the sender +of the messages. It also ensures that a user that sends an encrypted message does +so in the knowledge that the message may be shared with new members in the +room. +{{% /boxes/note %}} + +##### Construction and sharing of the key bundle + +Alice MAY choose not to share any room history (even messages sent when the +history visibity setting would allow sharing) if the current history +visibility setting does not allow sharing (i.e. if `history_visibility` is +set to `invited` or `joined`). + +Otherwise, before inviting Bob to a room, Alice constructs and sends a key bundle as follows: + +1. Alice SHOULD ensure that she has downloaded all keys relevant to the room + from [server-side key backup](#server-side-key-backups), if she is using it. + +2. Alice constructs a [`RoomKeyBundle`](#definition-roomkeybundle) structure, + containing the sessions she is aware of in the room. Alice SHOULD include + only [shareable encryption sessions](#shareable-encryption-sessions) in the + `room_keys` section of the structure; other sessions should be listed un the + with `withheld` section. + +3. Alice serialises the `RoomKeyBundle` as JSON. + +4. Alice encrypts and uploads the serialised JSON in the same way as when + [sending an encrypted attachment](#sending-encrypted-attachments). + +5. Alice ensures she has an up-to-date list of Bob's devices (performing a + [`/keys/query`](#post_matrixclientv3keysquery) request if necessary). + +6. For each of Bob's devices which are correctly + [cross-signed](#cross-signing), Alice encrypts and sends an + [`m.room_key_bundle`](#mroom_key_bundle) message. + +Alice MUST NOT send the `m.room.key_bundle` message to devices that have not +been correctly cross-signed by their owner, due to the risk of sharing +significant amounts of encrypted content with an attacker-controlled device. + +{{% definition path="api/client-server/definitions/room_key_bundle" %}} + +{{% event event="m.room_key_bundle" %}} + +##### Receiving a key bundle event + +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 and decrypt 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. + + The definition of "recently" is left up to clients. (They should consider + balancing the needs of (a) a user that closes their client just after + joining a room but before the bundle is imported, against (b) the overhead + of attempting to download a key bundle on every startup. 24 hours is a + recommended time limit.) + + * 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 large quantity of useless data. + +Once Bob has downloaded and decrypted the key bundle, the sessions are imported +as they would be when importing a [key export](#key-exports); however: + + * Only keys for the relevant room should be imported. Keys for other rooms + SHOULD be ignored. + + * Bob's client MUST remember who he received the keys from (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. + +Client implementations should note that server implementations may delete or +expire old media that appears unused. They must therefore gracefully handle +download failures due to the key bundle having expired (typically by just +giving up on the attempt to download the bundle, though they could also warn +the user.) + #### Messaging Algorithms ##### Messaging Algorithm Names diff --git a/data/api/client-server/definitions/room_key_bundle.yaml b/data/api/client-server/definitions/room_key_bundle.yaml new file mode 100644 index 00000000..3d6b3bcb --- /dev/null +++ b/data/api/client-server/definitions/room_key_bundle.yaml @@ -0,0 +1,104 @@ +# Copyright 2026 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: object +title: RoomKeyBundle +description: | + A bundle of room keys and withheld indications, sent from one user to another, to share encrypted history. + + A single session MUST NOT appear in both the `room_keys` and `withheld` + sections. Handling such malformed bundles as a receiving client is + implementation-defined. +properties: + room_keys: + description: | + The room keys to be shared with the recipient of the bundle. + + The data type is similar to the format used for [key exports](/client-server-api/#key-export-format), + but omits `forwarding_curve25519_key_chain` and `shared_history`. + type: array + items: + type: object + title: HistoricRoomKey + description: |- + The format of a session key, when shared as part of a `RoomKeyBundle`. + properties: + algorithm: + type: string + description: |- + The end-to-end message encryption algorithm that the key is for. Must be `m.megolm.v1.aes-sha2`. + example: "m.megolm.v1.aes-sha2" + room_id: + type: string + description: |- + The room where the session is used. + example: "!Cuyf34gef24t:localhost" + sender_claimed_keys: + type: object + additionalProperties: + type: string + description: |- + A map from algorithm name (`ed25519`) to the Ed25519 signing key of + the device which initiated the session originally, according to the + creator of this key bundle. + example: { "ed25519": "aj40p+aw64yPIdsxoog8jhPu9i7l7NcFRecuOQblE3Y" } + sender_key: + type: string + description: |- + Unpadded base64-encoded device Curve25519 key, of the device which + initiated the session originally, according to the creator of this + key bundle. + example: "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU" + session_id: + type: string + description: |- + The Megolm session ID. + example: "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ" + session_key: + type: string + description: |- + Unpadded base64-encoded session key in [session-export + format](https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/megolm.md#session-export-format). + example: "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8Llf..." + required: + - algorithm + - room_id + - sender_claimed_keys + - sender_key + - session_id + - session_key + withheld: + description: |- + The room keys that the creator of the bundle is choosing not to share + with the recipient. + + The `code` will normally be `m.history_not_shared`, to indicate that the + recipient isn't allowed to receive the key. + type: array + items: + allOf: + - $ref: "../../../event-schemas/schema/components/room_key_withheld_content.yaml" + - title: RoomKeyWithheld + example: { + "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" + } + + + + diff --git a/data/event-schemas/examples/m.room_key_bundle.yaml b/data/event-schemas/examples/m.room_key_bundle.yaml new file mode 100644 index 00000000..adfa29b9 --- /dev/null +++ b/data/event-schemas/examples/m.room_key_bundle.yaml @@ -0,0 +1,21 @@ +{ + "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" + } + } + } +} diff --git a/data/event-schemas/schema/m.room_key_bundle.yaml b/data/event-schemas/schema/m.room_key_bundle.yaml new file mode 100644 index 00000000..972c5c87 --- /dev/null +++ b/data/event-schemas/schema/m.room_key_bundle.yaml @@ -0,0 +1,40 @@ +--- +$schema: https://json-schema.org/draft/2020-12/schema + +allOf: + - $ref: core-event-schema/event.yaml + +description: |- + This event type is used to share a bundle of room keys when inviting a new user to the room. + + It is encrypted as an + [`m.room.encrypted`](/client-server-api/#mroomencrypted) [to-device + event](/client-server-api/#send-to-device-messaging) using + [Olm](/client-server-api/#molmv1curve25519-aes-sha2). + + The `sender_device_keys` property in the [Olm + plaintext](/client-server-api/#definition-olmpayload) MUST be + populated. Recipients SHOULD ignore `m.room_key_bundle` messages which omit + them. +properties: + content: + properties: + room_id: + type: string + description: The room to which the keys in the key bundle relate. + file: + description: |- + An [EncryptedFile](/client-server-api/#definition-encryptedfile) + structure. The location and decryption keys for the encrypted + [`RoomKeyBundle`](/client-server-api/#definition-roomkeybundle). + title: EncryptedFile + type: object + required: + - room_id + - file + type: object + type: + enum: + - m.room_key_bundle + type: string +type: object