From 6b7268ab472bf4dd68abb27b24bea5e5d4401136 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Fri, 28 Mar 2025 12:52:02 +0100 Subject: [PATCH 1/9] MSC4147: Including device keys with Olm-encrypted events Signed-off-by: Johannes Marbach --- .../modules/end_to_end_encryption.md | 37 +++----- .../definitions/olm_payload.yaml | 87 +++++++++++++++++++ 2 files changed, 101 insertions(+), 23 deletions(-) create mode 100644 data/api/client-server/definitions/olm_payload.yaml 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 49b053f6..29e3ab50 100644 --- a/content/client-server-api/modules/end_to_end_encryption.md +++ b/content/client-server-api/modules/end_to_end_encryption.md @@ -1512,20 +1512,7 @@ message. The plaintext payload is of the form: -```json -{ - "type": "", - "content": "", - "sender": "", - "recipient": "", - "recipient_keys": { - "ed25519": "" - }, - "keys": { - "ed25519": "" - } -} -``` +{{% definition path="api/client-server/definitions/olm_payload" %}} The type and content of the plaintext message event are given in the payload. @@ -1536,15 +1523,19 @@ 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. +Clients must ensure that the sending 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. To perform +this check, 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 under the `sender_device_keys` +property. Additionally, clients MUST also verify the signature of the keys. +If `sender_device_keys` is absent, clients MUST retrieve the sender's +keys from [`/keys/query`](#post_matrixclientv3keysquery) instead. This +will not allow them to verify key ownership if the sending device was +logged out or had its keys reset since sending the event. Therefore, +clients MUST populate the `sender_device_keys` property when sending +events themselves. If a client has multiple sessions established with another device, it should use the session from which it last received and successfully diff --git a/data/api/client-server/definitions/olm_payload.yaml b/data/api/client-server/definitions/olm_payload.yaml new file mode 100644 index 00000000..1d8e5055 --- /dev/null +++ b/data/api/client-server/definitions/olm_payload.yaml @@ -0,0 +1,87 @@ +# 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. +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": "", + "content": "", + "sender": "", + "recipient": "", + "recipient_keys": { + "ed25519": "" + }, + "keys": { + "ed25519": "" + }, + "sender_device_keys": { + "algorithms": ["", ""], + "user_id": "", + "device_id": "", + "keys": { + "ed25519:": "", + "curve25519:": "" + }, + "signatures": { + "": { + "ed25519:": "", + "ed25519:": "", + } + } + } +} From 00fdf603a9df72661dd7b0a9eaae4aeb1adccb8b Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Fri, 28 Mar 2025 12:55:10 +0100 Subject: [PATCH 2/9] Add changelog --- changelogs/client_server/newsfragments/2122.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/2122.feature diff --git a/changelogs/client_server/newsfragments/2122.feature b/changelogs/client_server/newsfragments/2122.feature new file mode 100644 index 00000000..0e299bad --- /dev/null +++ b/changelogs/client_server/newsfragments/2122.feature @@ -0,0 +1 @@ +Include device keys with Olm-encrypted events as per [MSC4147](https://github.com/matrix-org/matrix-spec-proposals/pull/4147). From 6656f00beef16bb08866d35b32daaeb3854a0023 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Fri, 28 Mar 2025 13:33:18 +0100 Subject: [PATCH 3/9] Add missing x-addedInMatrixVersion --- data/api/client-server/definitions/olm_payload.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/api/client-server/definitions/olm_payload.yaml b/data/api/client-server/definitions/olm_payload.yaml index 1d8e5055..8bac91e0 100644 --- a/data/api/client-server/definitions/olm_payload.yaml +++ b/data/api/client-server/definitions/olm_payload.yaml @@ -39,6 +39,7 @@ properties: sender_device_keys: $ref: device_keys.yaml description: The sender's device keys. + x-addedInMatrixVersion: "1.15" required: - type - content From 3f3c0ca0ba67437146333bbcd1cf9b70b4cb9d11 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Mon, 26 May 2025 11:05:36 +0200 Subject: [PATCH 4/9] Expand description of validation and verification --- .../modules/end_to_end_encryption.md | 74 ++++++++++++++----- 1 file changed, 56 insertions(+), 18 deletions(-) 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 29e3ab50..32ad8fa6 100644 --- a/content/client-server-api/modules/end_to_end_encryption.md +++ b/content/client-server-api/modules/end_to_end_encryption.md @@ -1517,25 +1517,63 @@ The plaintext payload is of the form: 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. +###### Validation of incoming decrypted events -Clients must ensure that the sending 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. To perform -this check, 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 under the `sender_device_keys` -property. Additionally, clients MUST also verify the signature of the keys. -If `sender_device_keys` is absent, clients MUST retrieve the sender's -keys from [`/keys/query`](#post_matrixclientv3keysquery) instead. This -will not allow them to verify key ownership if the sending device was -logged out or had its keys reset since sending the event. Therefore, -clients MUST populate the `sender_device_keys` property when sending -events themselves. +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:` must also match + the `sender_key` property in the cleartext `m.room.encrypted` + event body. + 3. `sender_device_keys.keys.curve25519:` 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:` (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 + +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 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. If a client has multiple sessions established with another device, it should use the session from which it last received and successfully From 9eba6b5c9e97ea25b7d762181b62cd235dea8365 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 28 May 2025 12:05:15 +0200 Subject: [PATCH 5/9] Update content/client-server-api/modules/end_to_end_encryption.md Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- content/client-server-api/modules/end_to_end_encryption.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 50d673b3..2729f512 100644 --- a/content/client-server-api/modules/end_to_end_encryption.md +++ b/content/client-server-api/modules/end_to_end_encryption.md @@ -1567,7 +1567,7 @@ 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 TOFU mechanism). + [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. From 63edffb29f6ad8d8aece92e953ecf866b90ef30f Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 28 May 2025 12:05:42 +0200 Subject: [PATCH 6/9] Update content/client-server-api/modules/end_to_end_encryption.md Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- content/client-server-api/modules/end_to_end_encryption.md | 2 ++ 1 file changed, 2 insertions(+) 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 2729f512..77118ab8 100644 --- a/content/client-server-api/modules/end_to_end_encryption.md +++ b/content/client-server-api/modules/end_to_end_encryption.md @@ -1519,6 +1519,8 @@ payload. ###### 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: From c0ac74e7fc0e59d084485afa236490698af0f596 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 28 May 2025 12:06:24 +0200 Subject: [PATCH 7/9] Update content/client-server-api/modules/end_to_end_encryption.md Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- content/client-server-api/modules/end_to_end_encryption.md | 2 ++ 1 file changed, 2 insertions(+) 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 77118ab8..b84f9dde 100644 --- a/content/client-server-api/modules/end_to_end_encryption.md +++ b/content/client-server-api/modules/end_to_end_encryption.md @@ -1549,6 +1549,8 @@ 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 From 910afe17b3257cc92420e6310cd04cdd70c1f8b7 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 28 May 2025 12:14:56 +0200 Subject: [PATCH 8/9] Update content/client-server-api/modules/end_to_end_encryption.md --- .../client-server-api/modules/end_to_end_encryption.md | 9 +++++++++ 1 file changed, 9 insertions(+) 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 b84f9dde..fc7fc668 100644 --- a/content/client-server-api/modules/end_to_end_encryption.md +++ b/content/client-server-api/modules/end_to_end_encryption.md @@ -1517,6 +1517,15 @@ The plaintext payload is of the form: The type and content of the plaintext message event are given in the payload. +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 +any messages should use its creation time as the time that it last +received a message. A client may expire old sessions by defining a +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. From ed878f1c14396f4a3b71655a3561cb63bdba505e Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 28 May 2025 12:16:25 +0200 Subject: [PATCH 9/9] Update end_to_end_encryption.md --- .../client-server-api/modules/end_to_end_encryption.md | 9 --------- 1 file changed, 9 deletions(-) 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 fc7fc668..2c275089 100644 --- a/content/client-server-api/modules/end_to_end_encryption.md +++ b/content/client-server-api/modules/end_to_end_encryption.md @@ -1588,15 +1588,6 @@ 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. -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 -any messages should use its creation time as the time that it last -received a message. A client may expire old sessions by defining a -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. - ###### Recovering from undecryptable messages Occasionally messages may be undecryptable by clients due to a variety