Basic spec for history sharing

This commit is contained in:
Richard van der Hoff 2026-06-12 16:51:16 +01:00
parent f48bf2e9eb
commit 1efc0623fa
4 changed files with 302 additions and 0 deletions

View file

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

View file

@ -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"
}

View file

@ -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"
}
}
}
}

View file

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