Skip to content

Spec PR - MSC4147: Including device keys with Olm-encrypted events #2122

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

Merged
merged 11 commits into from
May 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelogs/client_server/newsfragments/2122.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Include device keys with Olm-encrypted events as per [MSC4147](https://github.com/matrix-org/matrix-spec-proposals/pull/4147).
93 changes: 63 additions & 30 deletions content/client-server-api/modules/end_to_end_encryption.md
Original file line number Diff line number Diff line change
Expand Up @@ -1512,40 +1512,11 @@ message.

The plaintext payload is of the form:

```json
{
"type": "<type of the plaintext event>",
"content": "<content for the plaintext event>",
"sender": "<sender_user_id>",
"recipient": "<recipient_user_id>",
"recipient_keys": {
"ed25519": "<our_ed25519_key>"
},
"keys": {
"ed25519": "<sender_ed25519_key>"
}
}
```
{{% definition path="api/client-server/definitions/olm_payload" %}}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I replaced the example with a schema because that allows us to reuse device_keys.yaml.

Copy link
Member

Choose a reason for hiding this comment

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

(and it's just generally Better. We're trying to phase out these old-style json blocks)


The type and content of the plaintext message event are given in the
payload.

Other properties are included in order to prevent an attacker from
publishing someone else's curve25519 keys as their own and subsequently
claiming to have sent messages which they didn't. `sender` must
correspond to the user who sent the event, `recipient` to the local
user, and `recipient_keys` to the local ed25519 key.

Clients must confirm that the `sender_key` property in the cleartext
`m.room.encrypted` event body, and the `keys.ed25519` property in the
decrypted plaintext, match the keys returned by
[`/keys/query`](#post_matrixclientv3keysquery) for
the given user. Clients must also verify the signature of the keys from the
`/keys/query` response. Without this check, a client cannot be sure that
the sender device owns the private part of the ed25519 key it claims to
have in the Olm payload. This is crucial when the ed25519 key corresponds
to a verified device.

If a client has multiple sessions established with another device, it
should use the session from which it last received and successfully
decrypted a message. For these purposes, a session that has not received
Expand All @@ -1555,6 +1526,68 @@ maximum number of olm sessions that it will maintain for each device,
and expiring sessions on a Least Recently Used basis. The maximum number
of olm sessions maintained per device should be at least 4.

###### Validation of incoming decrypted events

{{% changed-in v="1.15" %}} Existing checks made more explicit, and checks for `sender_device_keys` added.

After decrypting an incoming encrypted event, clients MUST apply the
following checks:

1. The `sender` property in the decrypted content must match the
`sender` of the event.
2. The `keys.ed25519` property in the decrypted content must match
the `sender_key` property in the cleartext `m.room.encrypted`
event body.
3. The `recipient` property in the decrypted content must match
the user ID of the local user.
4. The `recipient_keys.ed25519` property in the decrypted content
must match the client device's [Ed25519 signing key](#device-keys).
5. Where `sender_device_keys` is present in the decrypted content:
1. `sender_device_keys.user_id` must also match the `sender`
of the event.
2. `sender_device_keys.keys.ed25519:<device_id>` must also match
the `sender_key` property in the cleartext `m.room.encrypted`
event body.
3. `sender_device_keys.keys.curve25519:<device_id>` must match
the Curve25519 key used to establish the Olm session.
4. The `sender_device_keys` structure must have a valid signature
from the key with ID `ed25519:<device_id>` (i.e., the sending
device's Ed25519 key).

Any event that does not comply with these checks MUST be discarded.

###### Verification of the sending user for incoming events

{{% added-in v="1.15" %}}

In addition, for each Olm session, clients MUST verify that the
Curve25519 key used to establish the Olm session does indeed belong
to the claimed `sender`. This requires a signed "device keys" structure
for that Curve25519 key, which can be obtained in one of two ways:

1. An Olm message may be received with a `sender_device_keys` property
in the decrypted content.
2. The keys are returned via a [`/keys/query`](#post_matrixclientv3keysquery)
request. Note that both the Curve25519 key **and** the Ed25519 key in
the returned device keys structure must match those used in an
Olm-encrypted event as above. (In particular, the Ed25519 key must
be present in the **encrypted** content of an Olm-encrypted event
to prevent an attacker from claiming another user's Curve25519 key
as their own.)

Ownership of the Curve25519 key is then established in one of two ways:

1. Via [cross-signing](#cross-signing). For this to be sufficient, the
device keys structure must be signed by the sender's self-signing key,
and that self-signing key must itself have been validated (either via
[explicit verification](#device-verification) or a "trust on first use" (TOFU) mechanism).
2. Via explicit verification of the device's Ed25519 signing key, as
contained in the device keys structure. This is no longer recommended.

A failure to complete these verifications does not necessarily mean that
the session is bogus; however it is the case that there is no proof that
the claimed sender is accurate, and the user should be warned accordingly.

###### Recovering from undecryptable messages

Occasionally messages may be undecryptable by clients due to a variety
Expand Down
88 changes: 88 additions & 0 deletions data/api/client-server/definitions/olm_payload.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Copyright 2025 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: OlmPayload
description: |-
The plaintext payload of Olm message events.
properties:
type:
type: string
description: The type of the event.
content:
type: object
description: The event content.
sender:
type: string
description: The user ID of the event sender.
recipient:
type: string
description: The user ID of the intended event recipient.
recipient_keys:
description: The recipient's signing keys of the encrypted event.
$ref: "#/components/schemas/SigningKeys"
keys:
$ref: "#/components/schemas/SigningKeys"
description: The sender's signing keys of the encrypted event.
sender_device_keys:
$ref: device_keys.yaml
description: The sender's device keys.
x-addedInMatrixVersion: "1.15"
required:
- type
- content
- sender
- recipient
- recipient_keys
- keys
components:
schemas:
SigningKeys:
type: object
title: SigningKeys
description: Public keys used for an `m.olm.v1.curve25519-aes-sha2` event.
properties:
ed25519:
type: string
description: The Ed25519 public key encoded using unpadded base64.
required:
- ed25519
example: {
"type": "<type of the plaintext event>",
"content": "<content for the plaintext event>",
"sender": "<sender_user_id>",
"recipient": "<recipient_user_id>",
"recipient_keys": {
"ed25519": "<our_ed25519_key>"
},
"keys": {
"ed25519": "<sender_ed25519_key>"
},
"sender_device_keys": {
"algorithms": ["<supported>", "<algorithms>"],
"user_id": "<user_id>",
"device_id": "<device_id>",
"keys": {
"ed25519:<device_id>": "<sender_ed25519_key>",
"curve25519:<device_id>": "<sender_curve25519_key>"
},
"signatures": {
"<user_id>": {
"ed25519:<device_id>": "<device_signature>",
"ed25519:<ssk_id>": "<ssk_signature>",
}
}
}
}