Compare commits

...

12 commits

Author SHA1 Message Date
gewitternacht d1920f840c
Merge 02ce04f083 into 4c2bb5aae1 2026-07-20 11:10:41 +02:00
gewitternacht 02ce04f083
clarify that replacement event is Megolm message plaintext
Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2026-07-20 11:10:38 +02:00
Johanna Stuber a692b7be83 link to m.room.encrypted for Olm and Megolm content
Signed-off-by: Johanna Stuber <johannas@element.io>
2026-07-20 10:51:07 +02:00
networkException 4c2bb5aae1
Clarify user avatar_urls to always use MXC URIs (#2422)
Some checks failed
Spec / 🔎 Validate OpenAPI specifications (push) Has been cancelled
Spec / 🔎 Check Event schema examples (push) Has been cancelled
Spec / 🔎 Check OpenAPI definitions examples (push) Has been cancelled
Spec / 🔎 Check JSON Schemas inline examples (push) Has been cancelled
Spec / ⚙️ Calculate baseURL for later jobs (push) Has been cancelled
Spec / 📢 Run towncrier for changelog (push) Has been cancelled
Spell Check / Spell Check with Typos (push) Has been cancelled
Spec / 🐍 Build OpenAPI definitions (push) Has been cancelled
Spec / 📖 Build the spec (push) Has been cancelled
Spec / 🔎 Validate generated HTML (push) Has been cancelled
Spec / 📖 Build the historical backup spec (push) Has been cancelled
Spec / Create release (push) Has been cancelled
Previously the spec would use a mix of `string`, `URI`
and `MXC URI` for values of a user's `avatar_url` in
different apis (like `m.room.member`, `m.presence`,
CS profile endpoints).

This clarification updates all occurances to be MXC URIs,
prompted by https URIs `m.room.member#avatar_url` events
in the wild.

Signed-off-by: networkException <git@nwex.de>
2026-07-17 13:27:19 +02:00
gewitternacht f9dec5dc92
Fix typesetting of symbols in Olm/Megolm spec (#2421)
Some checks are pending
Spec / 🔎 Validate OpenAPI specifications (push) Waiting to run
Spec / 🔎 Check Event schema examples (push) Waiting to run
Spec / 🔎 Check OpenAPI definitions examples (push) Waiting to run
Spec / 🔎 Check JSON Schemas inline examples (push) Waiting to run
Spec / ⚙️ Calculate baseURL for later jobs (push) Waiting to run
Spec / 🐍 Build OpenAPI definitions (push) Blocked by required conditions
Spec / 📢 Run towncrier for changelog (push) Waiting to run
Spec / 📖 Build the spec (push) Blocked by required conditions
Spec / 🔎 Validate generated HTML (push) Blocked by required conditions
Spec / 📖 Build the historical backup spec (push) Blocked by required conditions
Spec / Create release (push) Blocked by required conditions
Spell Check / Spell Check with Typos (push) Waiting to run
* properly typeset symbols in Olm/Megolm message format tables

Signed-off-by: Johanna Stuber <johannas@element.io>

* use \mathit in Olm spec analogously to Megolm spec

Signed-off-by: Johanna Stuber <johannas@element.io>

* add newsfragment

Signed-off-by: Johanna Stuber <johannas@element.io>

* fix typo

Signed-off-by: Johanna Stuber <johannas@element.io>

---------

Signed-off-by: Johanna Stuber <johannas@element.io>
2026-07-16 11:55:30 -04:00
Johannes Marbach 16b04f9d6c
Add further normative language in mutual rooms server behaviour (#2407)
* Add further normative language in mutual rooms server behaviour

Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>

* Avoid superfluous MUST

Co-authored-by: Hubert Chathi <hubertc@matrix.org>

---------

Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Hubert Chathi <hubertc@matrix.org>
2026-07-16 11:18:09 -04:00
Johanna Stuber cece4c28ec also rename encrypted_payload_base_64 in event_replacements
Signed-off-by: Johanna Stuber <johannas@element.io>
2026-07-15 10:21:11 +02:00
Johanna Stuber 3e6c1e35d9 avoid 'message body' for Megolm too
Signed-off-by: Johanna Stuber <johannas@element.io>
2026-07-14 09:43:17 +02:00
Johanna Stuber 409d052b79 change 'pre-key message' to plural
Signed-off-by: Johanna Stuber <johannas@element.io>
2026-07-14 09:37:20 +02:00
Johanna Stuber 22dfc249b6 reorder explanation of and , avoid 'message body'
Signed-off-by: Johanna Stuber <johannas@element.io>
2026-07-14 09:32:08 +02:00
Johanna Stuber c53c2d093a add newsfragment
Signed-off-by: Johanna Stuber <johannas@element.io>
2026-07-13 17:50:02 +02:00
Johanna Stuber 1b62e3d515 improve event format description of m.room.encrypted events
Signed-off-by: Johanna Stuber <johannas@element.io>
2026-07-13 17:23:08 +02:00
16 changed files with 56 additions and 40 deletions

View file

@ -0,0 +1 @@
Add further normative language in mutual rooms server behaviour.

View file

@ -0,0 +1 @@
Improve the format description of `m.room.encrypted` (Olm/Megolm) events.

View file

@ -0,0 +1 @@
Use MXC URI type for all user `avatar_url` fields. Contributed by @networkException.

View file

@ -0,0 +1 @@
Fix typesetting of some symbols in the Olm/Megolm spec.

View file

@ -1723,28 +1723,30 @@ Devices that support Olm must include "m.olm.v1.curve25519-aes-sha2" in
their list of supported messaging algorithms, must list a Curve25519 their list of supported messaging algorithms, must list a Curve25519
device key, and must publish Curve25519 one-time keys. device key, and must publish Curve25519 one-time keys.
An event encrypted using Olm has the following format: The content of an [`m.room.encrypted`](#mroomencrypted) event using Olm has the following format:
```json ```json
{ {
"type": "m.room.encrypted",
"content": { "content": {
"algorithm": "m.olm.v1.curve25519-aes-sha2", "algorithm": "m.olm.v1.curve25519-aes-sha2",
"sender_key": "<sender_curve25519_key>", "sender_key": "<sender_curve25519_key>",
"ciphertext": { "ciphertext": {
"<device_curve25519_key>": { "<device_curve25519_key>": {
"type": 0, "type": 0,
"body": "<encrypted_payload_base_64>" "body": "<base64_encoded_olm_message>"
} }
} }
} }
} }
``` ```
Note that when the event is received from the server, it will have a `type`
`ciphertext` is a mapping from device Curve25519 key to an encrypted (with a value of `m.room.encrypted`) and `sender` property alongside the
payload for that device. `body` is a Base64-encoded [Olm message body](/olm-megolm/olm/#the-olm-message-format). `content` property. In `content`, `ciphertext` is a mapping from a device
`type` is an integer indicating the type of the message body: 0 for the Curve25519 key to an object with a `type` and a `body`. Here, `body` is a
initial [pre-key message](/olm-megolm/olm/#pre-key-messages), 1 for [normal messages](/olm-megolm/olm/#normal-messages). Base64-encoded [Olm message](/olm-megolm/olm/#the-olm-message-format), and
`type` is an integer indicating the type of the message:
0 for the initial [pre-key messages](/olm-megolm/olm/#pre-key-messages),
1 for [normal messages](/olm-megolm/olm/#normal-messages).
Olm sessions will generate messages with a type of 0 until they receive Olm sessions will generate messages with a type of 0 until they receive
a message. Once a session has decrypted a message it will produce a message. Once a session has decrypted a message it will produce
@ -1891,23 +1893,24 @@ This uses:
Devices that support Megolm must support Olm, and include Devices that support Megolm must support Olm, and include
"m.megolm.v1.aes-sha2" in their list of supported messaging algorithms. "m.megolm.v1.aes-sha2" in their list of supported messaging algorithms.
An event encrypted using Megolm has the following format: The content of an [`m.room.encrypted`](#mroomencrypted) event using Megolm has the following format:
```json ```json
{ {
"type": "m.room.encrypted",
"content": { "content": {
"algorithm": "m.megolm.v1.aes-sha2", "algorithm": "m.megolm.v1.aes-sha2",
"sender_key": "<sender_curve25519_key>", "sender_key": "<sender_curve25519_key>",
"device_id": "<sender_device_id>", "device_id": "<sender_device_id>",
"session_id": "<outbound_group_session_id>", "session_id": "<outbound_group_session_id>",
"ciphertext": "<encrypted_payload_base_64>" "ciphertext": "<base64_encoded_megolm_message>"
} }
} }
``` ```
Note that when the event is received from the server, it will have additional
The encrypted payload can contain any message event. The plaintext is of properties alongside the `content` property, including a `type` (with a value
the form: of `m.room.encrypted`) and a `sender` property. In `content`, `ciphertext`
is a Base64-encoded [Megolm message](/olm-megolm/megolm/#message-format),
whose plaintext body is of the form:
```json ```json
{ {
@ -1917,7 +1920,7 @@ the form:
} }
``` ```
We include the room ID in the payload, because otherwise the homeserver We include the room ID in the encrypted message, because otherwise the homeserver
would be able to change the room a message was sent in. would be able to change the room a message was sent in.
Clients must guard against replay attacks by keeping track of the Clients must guard against replay attacks by keeping track of the

View file

@ -91,13 +91,13 @@ For example, a replacement for an encrypted event might look like this:
"sender_key": "<sender_curve25519_key>", "sender_key": "<sender_curve25519_key>",
"device_id": "<sender_device_id>", "device_id": "<sender_device_id>",
"session_id": "<outbound_group_session_id>", "session_id": "<outbound_group_session_id>",
"ciphertext": "<encrypted_payload_base_64>" "ciphertext": "<base64_encoded_megolm_message>"
} }
// irrelevant fields not shown // irrelevant fields not shown
} }
``` ```
... and, once decrypted, the payload might look like this: ... and the plaintext body of the Megolm message might look like this:
```json ```json
{ {

View file

@ -6,9 +6,9 @@
#### Server behaviour #### Server behaviour
The server may decide that the response to this endpoint is too large, and only return a The server MAY decide that the response to this endpoint is too large, and only return a
subset of the results. In this case, the server should populate the optional field `next_batch` subset of the results. In this case, the server populates the optional field `next_batch`
with an [opaque identifier](/appendices/#opaque-identifiers). The client may then supply with an [opaque identifier](/appendices/#opaque-identifiers). The client can then supply
the identifier as the `from` query parameter in a subsequent request, along with the original the identifier as the `from` query parameter in a subsequent request, along with the original
`user_id`, to fetch the next batch of responses. This will continue until the server no longer `user_id`, to fetch the next batch of responses. This will continue until the server no longer
inserts `next_batch`, meaning there are no further results. inserts `next_batch`, meaning there are no further results.

View file

@ -257,8 +257,8 @@ consists of the following key-value pairs:
**Name**|**Tag**|**Type**|**Meaning** **Name**|**Tag**|**Type**|**Meaning**
:-----:|:-----:|:-----:|:-----: :-----:|:-----:|:-----:|:-----:
Message-Index|0x08|Integer|The index of the ratchet, i Message-Index|0x08|Integer|The index of the ratchet, \(i\).
Cipher-Text|0x12|String|The cipher-text, Xi, of the message Cipher-Text|0x12|String|The cipher-text of the message, \(X_i\).
Within the payload, integers are encoded using a variable length encoding. Each Within the payload, integers are encoded using a variable length encoding. Each
integer is encoded as a sequence of bytes with the high bit set followed by a integer is encoded as a sequence of bytes with the high bit set followed by a

View file

@ -17,12 +17,12 @@ side of an \(=\) it means that the output is split.
When this document uses \(\operatorname{ECDH}\left(K_A,K_B\right)\) it means When this document uses \(\operatorname{ECDH}\left(K_A,K_B\right)\) it means
that each party computes a Diffie-Hellman agreement using their private key that each party computes a Diffie-Hellman agreement using their private key
and the remote party's public key. and the remote party's public key.
So party \(A\) computes \(\operatorname{ECDH}\left(K_B^{public},K_A^{private}\right)\) So party \(A\) computes \(\operatorname{ECDH}\left(K_B^{\mathit{public}},K_A^{\mathit{private}}\right)\)
and party \(B\) computes \(\operatorname{ECDH}\left(K_A^{public},K_B^{private}\right)\). and party \(B\) computes \(\operatorname{ECDH}\left(K_A^{\mathit{public}},K_B^{\mathit{private}}\right)\).
Where this document uses \(\operatorname{HKDF}\left(salt,IKM,info,L\right)\) it Where this document uses \(\operatorname{HKDF}\left(\mathit{salt},\mathit{IKM},\mathit{info},L\right)\) it
refers to the [HMAC-based key derivation function][] with a salt value of refers to the [HMAC-based key derivation function][] with a salt value of
\(salt\), input key material of \(IKM\), context string \(info\), \(\mathit{salt}\), input key material of \(\mathit{IKM}\), context string \(\mathit{info}\),
and output keying material length of \(L\) bytes. and output keying material length of \(L\) bytes.
## The Olm Algorithm ## The Olm Algorithm
@ -226,9 +226,9 @@ significant bits are stored in the first byte.
**Name**|**Tag**|**Type**|**Meaning** **Name**|**Tag**|**Type**|**Meaning**
:-----:|:-----:|:-----:|:-----: :-----:|:-----:|:-----:|:-----:
Ratchet-Key|0x0A|String|The public part of the ratchet key, Ti, of the message Ratchet-Key|0x0A|String|The public part of the ratchet key of the message, \(T_i\).
Chain-Index|0x10|Integer|The chain index, j, of the message Chain-Index|0x10|Integer|The chain index of the message, \(j\).
Cipher-Text|0x22|String|The cipher-text, Xi,j, of the message Cipher-Text|0x22|String|The cipher-text of the message, \(X_{i,j}\).
The length of the MAC is determined by the authenticated encryption algorithm The length of the MAC is determined by the authenticated encryption algorithm
being used. (Olm version 1 uses [HMAC-SHA-256][], truncated to 8 bytes). The being used. (Olm version 1 uses [HMAC-SHA-256][], truncated to 8 bytes). The
@ -251,9 +251,9 @@ The payload uses the same key-value format as for normal messages.
**Name**|**Tag**|**Type**|**Meaning** **Name**|**Tag**|**Type**|**Meaning**
:-----:|:-----:|:-----:|:-----: :-----:|:-----:|:-----:|:-----:
One-Time-Key|0x0A|String|The public part of Bob's single-use key, Eb. One-Time-Key|0x0A|String|The public part of Bob's single-use key, \(E_B\).
Base-Key|0x12|String|The public part of Alice's single-use key, Ea. Base-Key|0x12|String|The public part of Alice's single-use key, \(E_A\).
Identity-Key|0x1A|String|The public part of Alice's identity key, Ia. Identity-Key|0x1A|String|The public part of Alice's identity key, \(I_A\).
Message|0x22|String|An embedded Olm message with its own version and MAC. Message|0x22|String|An embedded Olm message with its own version and MAC.
## Olm Authenticated Encryption ## Olm Authenticated Encryption
@ -268,13 +268,13 @@ message key using [HKDF-SHA-256][] using the default salt and an info of
\[ \[
\begin{aligned} \begin{aligned}
AES\_KEY_{i,j}\;\parallel\;HMAC\_KEY_{i,j}\;\parallel\;AES\_IV_{i,j} \mathit{AES\_KEY}_{i,j}\;\parallel\;\mathit{HMAC\_KEY}_{i,j}\;\parallel\;\mathit{AES\_IV}_{i,j}
&= \operatorname{HKDF}\left(0,M_{i,j},\text{``OLM\_KEYS"},80\right) &= \operatorname{HKDF}\left(0,M_{i,j},\text{``OLM\_KEYS"},80\right)
\end{aligned} \end{aligned}
\] \]
The plain-text is encrypted with AES-256, using the key \(AES\_KEY_{i,j}\) The plain-text is encrypted with AES-256, using the key \(\mathit{AES\_KEY}_{i,j}\)
and the IV \(AES\_IV_{i,j}\) to give the cipher-text, \(X_{i,j}\). and the IV \(\mathit{AES\_IV}_{i,j}\) to give the cipher-text, \(X_{i,j}\).
Then the entire message (including the Version Byte and all Payload Bytes) are Then the entire message (including the Version Byte and all Payload Bytes) are
passed through [HMAC-SHA-256][]. The first 8 bytes of the MAC are appended to the message. passed through [HMAC-SHA-256][]. The first 8 bytes of the MAC are appended to the message.

View file

@ -54,7 +54,8 @@ properties:
example: true example: true
avatar_url: avatar_url:
type: string type: string
format: uri format: mx-mxc-uri
pattern: "^mxc:\\/\\/"
description: The URL for the room's avatar, if one is set. description: The URL for the room's avatar, if one is set.
example: "mxc://example.org/abcdef" example: "mxc://example.org/abcdef"
join_rule: join_rule:

View file

@ -340,7 +340,8 @@ paths:
description: The display name of the user this object is representing. description: The display name of the user this object is representing.
avatar_url: avatar_url:
type: string type: string
format: uri format: mx-mxc-uri
pattern: "^mxc:\\/\\/"
description: The avatar of the user this object is representing, as an [`mxc://` description: The avatar of the user this object is representing, as an [`mxc://`
URI](/client-server-api/#matrix-content-mxc-uris). URI](/client-server-api/#matrix-content-mxc-uris).
description: A map from user ID to a RoomMember object. description: A map from user ID to a RoomMember object.

View file

@ -238,7 +238,8 @@ paths:
title: Display name title: Display name
avatar_url: avatar_url:
type: string type: string
format: uri format: mx-mxc-uri
pattern: "^mxc:\\/\\/"
title: Avatar Url title: Avatar Url
events_before: events_before:
type: array type: array

View file

@ -90,7 +90,8 @@ paths:
description: The display name of the user, if one exists. description: The display name of the user, if one exists.
avatar_url: avatar_url:
type: string type: string
format: uri format: mx-mxc-uri
pattern: "^mxc:\\/\\/"
example: mxc://bar.com/foo example: mxc://bar.com/foo
description: The avatar url, as an [`mxc://` description: The avatar url, as an [`mxc://`
URI](/client-server-api/#matrix-content-mxc-uris), URI](/client-server-api/#matrix-content-mxc-uris),

View file

@ -172,6 +172,8 @@ paths:
example: John Doe example: John Doe
avatar_url: avatar_url:
type: string type: string
format: mx-mxc-uri
pattern: "^mxc:\\/\\/"
description: |- description: |-
The avatar URL for the user's avatar. MUST either be omitted or set to The avatar URL for the user's avatar. MUST either be omitted or set to
`null` if the user does not have an avatar set. `null` if the user does not have an avatar set.

View file

@ -12,6 +12,8 @@
"properties": { "properties": {
"avatar_url": { "avatar_url": {
"type": "string", "type": "string",
"format": "mx-mxc-uri",
"pattern": "^mxc:\\/\\/",
"description": "The current avatar URL for this user, if any." "description": "The current avatar URL for this user, if any."
}, },
"displayname": { "displayname": {

View file

@ -53,7 +53,8 @@ properties:
avatar_url: avatar_url:
description: 'The avatar URL for this user, if any.' description: 'The avatar URL for this user, if any.'
type: string type: string
format: uri format: mx-mxc-uri
pattern: "^mxc:\\/\\/"
displayname: displayname:
description: 'The display name for this user, if any.' description: 'The display name for this user, if any.'
type: type: