This module adds `file` and `thumbnail_file` properties, of type
`EncryptedFile`, to `m.room.message` msgtypes that reference files, such
as [m.file]() and [m.image](), replacing the `url` and `thumbnail_url`
properties.
`EncryptedFile`
<table>
<thead>
<trclass="header">
<th>Parameter</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td>url</td>
<td>string</td>
<td><strong>Required.</strong> The URL to the file.</td>
</tr>
<trclass="even">
<td>key</td>
<td>JWK</td>
<td><strong>Required.</strong> A <ahref="https://tools.ietf.org/html/rfc7517#appendix-A.3">JSON Web Key</a> object.</td>
</tr>
<trclass="odd">
<td><p>iv</p></td>
<td><p>string</p></td>
<td><p><strong>Required.</strong> The 128-bit unique counter block used by AES-CTR, encoded as unpadded base64.</p></td>
</tr>
<trclass="even">
<td><p>hashes</p></td>
<td><p>{string: string}</p></td>
<td><p><strong>Required.</strong> A map from an algorithm name to a hash of the ciphertext, encoded as unpadded base64. Clients should support the SHA-256 hash, which uses the key <code>sha256</code>.</p></td>
</tr>
<trclass="odd">
<td><p>v</p></td>
<td><p>string</p></td>
<td><p><strong>Required.</strong> Version of the encrypted attachments protocol. Must be <code>v2</code>.</p></td>
</tr>
</tbody>
</table>
`JWK`
<table>
<thead>
<trclass="header">
<th>Parameter</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td>kty</td>
<td>string</td>
<td><strong>Required.</strong> Key type. Must be <code>oct</code>.</td>
</tr>
<trclass="even">
<td><p>key_ops</p></td>
<td><p>[string]</p></td>
<td><p><strong>Required.</strong> Key operations. Must at least contain <code>encrypt</code> and <code>decrypt</code>.</p></td>
</tr>
<trclass="odd">
<td>alg</td>
<td>string</td>
<td><strong>Required.</strong> Algorithm. Must be <code>A256CTR</code>.</td>
</tr>
<trclass="even">
<td>k</td>
<td>string</td>
<td><strong>Required.</strong> The key, encoded as urlsafe unpadded base64.</td>
</tr>
<trclass="odd">
<td><p>ext</p></td>
<td><p>boolean</p></td>
<td><p><strong>Required.</strong> Extractable. Must be <code>true</code>. This is a <ahref="https://w3c.github.io/webcrypto/#iana-section-jwk">W3C extension</a>.</p></td>
</tr>
</tbody>
</table>
Example:
##### Claiming one-time keys
A client wanting to set up a session with another device can claim a
one-time key for that device. This is done by making a request to the
`/keys/claim`\_ API.
A homeserver should rate-limit the number of one-time keys that a given
user or remote server can claim. A homeserver should discard the public
part of a one time key once it has given that key to another user.
#### Device verification
Before Alice sends Bob encrypted data, or trusts data received from him,
she may want to verify that she is actually communicating with him,
rather than a man-in-the-middle. This verification process requires an
out-of-band channel: there is no way to do it within Matrix without
trusting the administrators of the homeservers.
In Matrix, verification works by Alice meeting Bob in person, or
contacting him via some other trusted medium, and use [SAS
Verification](#SAS Verification) to interactively verify Bob's devices.
Alice and Bob may also read aloud their unpadded base64 encoded Ed25519
public key, as returned by `/keys/query`.
Device verification may reach one of several conclusions. For example:
- Alice may "accept" the device. This means that she is satisfied that
the device belongs to Bob. She can then encrypt sensitive material
for that device, and knows that messages received were sent from
that device.
- Alice may "reject" the device. She will do this if she knows or
suspects that Bob does not control that device (or equivalently,
does not trust Bob). She will not send sensitive material to that
device, and cannot trust messages apparently received from it.
- Alice may choose to skip the device verification process. She is not
able to verify that the device actually belongs to Bob, but has no
reason to suspect otherwise. The encryption protocol continues to
protect against passive eavesdroppers.
Note
Once the signing key has been verified, it is then up to the encryption
protocol to verify that a given message was sent from a device holding
that Ed25519 private key, or to encrypt a message so that it may only be
decrypted by such a device. For the Olm protocol, this is documented at
<https://matrix.org/docs/olm_signing.html>.
##### Key verification framework
Verifying keys manually by reading out the Ed25519 key is not very
user-friendly, and can lead to errors. In order to help mitigate errors,
and to make the process easier for users, some verification methods are
supported by the specification. The methods all use a common framework
for negotiating the key verification.
To use this framework, Alice's client would send
`m.key.verification.request` events to Bob's devices. All of the
`to_device` messages sent to Bob MUST have the same `transaction_id` to
indicate they are part of the same request. This allows Bob to reject
the request on one device, and have it apply to all of his devices.
Similarly, it allows Bob to process the verification on one device
without having to involve all of his devices.
When Bob's device receives an `m.key.verification.request`, it should
prompt Bob to verify keys with Alice using one of the supported methods
in the request. If Bob's device does not understand any of the methods,
it should not cancel the request as one of his other devices may support
the request. Instead, Bob's device should tell Bob that an unsupported
method was used for starting key verification. The prompt for Bob to
accept/reject Alice's request (or the unsupported method prompt) should
be automatically dismissed 10 minutes after the `timestamp` field or 2
minutes after Bob's client receives the message, whichever comes first,
if Bob does not interact with the prompt. The prompt should additionally
be hidden if an appropriate `m.key.verification.cancel` message is
received.
If Bob rejects the request, Bob's client must send an
`m.key.verification.cancel` message to Alice's device. Upon receipt,
Alice's device should tell her that Bob does not want to verify her
device and send `m.key.verification.cancel` messages to all of Bob's
devices to notify them that the request was rejected.
If Bob accepts the request, Bob's device starts the key verification
process by sending an `m.key.verification.start` message to Alice's
device. Upon receipt of this message, Alice's device should send an
`m.key.verification.cancel` message to all of Bob's other devices to
indicate the process has been started. The start message must use the
same `transaction_id` from the original key verification request if it
is in response to the request. The start message can be sent
independently of any request.
Individual verification methods may add additional steps, events, and
properties to the verification messages. Event types for methods defined
in this specification must be under the `m.key.verification` namespace
and any other event types must be namespaced according to the Java
package naming convention.
Any of Alice's or Bob's devices can cancel the key verification request
or process at any time with an `m.key.verification.cancel` message to
all applicable devices.
This framework yields the following handshake, assuming both Alice and
Bob each have 2 devices, Bob's first device accepts the key verification
request, and Alice's second device initiates the request. Note how
Alice's first device is not involved in the request or verification
When a backup is created with the `algorithm` set to
`m.megolm_backup.v1.curve25519-aes-sha2`, the `auth_data` should have
the following format:
`AuthData`
<table>
<thead>
<trclass="header">
<th>Parameter</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td><p>public_key</p></td>
<td><p>string</p></td>
<td><p><strong>Required.</strong> The curve25519 public key used to encrypt the backups, encoded in unpadded base64.</p></td>
</tr>
<trclass="even">
<td><p>signatures</p></td>
<td><p>Signatures</p></td>
<td><p>Optional. Signatures of the <code>auth_data</code>, as Signed JSON</p></td>
</tr>
</tbody>
</table>
The `session_data` field in the backups is constructed as follows:
1. Encode the session key to be backed up as a JSON object with the
properties:
<table>
<thead>
<trclass="header">
<th>Parameter</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td><p>algorithm</p></td>
<td><p>string</p></td>
<td><p><strong>Required.</strong> The end-to-end message encryption algorithm that the key is for. Must be <code>m.megolm.v1.aes-sha2</code>.</p></td>
</tr>
<trclass="even">
<td><p>forwarding_curve25519_key_chain</p></td>
<td><p>[string]</p></td>
<td><p><strong>Required.</strong> Chain of Curve25519 keys through which this session was forwarded, via <ahref="">m.forwarded_room_key</a> events.</p></td>
is too long despite giving a more precise description of the algorithm:
it adds to the data transfer overhead and sacrifices clarity for human
readers without adding any useful extra information.
##### `m.olm.v1.curve25519-aes-sha2`
The name `m.olm.v1.curve25519-aes-sha2` corresponds to version 1 of the
Olm ratchet, as defined by the [Olm
specification](http://matrix.org/docs/spec/olm.html). This uses:
- Curve25519 for the initial key agreement.
- HKDF-SHA-256 for ratchet key derivation.
- Curve25519 for the root key ratchet.
- HMAC-SHA-256 for the chain key ratchet.
- HKDF-SHA-256, AES-256 in CBC mode, and 8 byte truncated HMAC-SHA-256
for authenticated encryption.
Devices that support Olm must include "m.olm.v1.curve25519-aes-sha2" in
their list of supported messaging algorithms, must list a Curve25519
device key, and must publish Curve25519 one-time keys.
An event encrypted using Olm has the following format:
{
"type": "m.room.encrypted",
"content": {
"algorithm": "m.olm.v1.curve25519-aes-sha2",
"sender_key": "<sender_curve25519_key>",
"ciphertext": {
"<device_curve25519_key>": {
"type": 0,
"body": "<encrypted_payload_base_64>"
}
}
}
}
`ciphertext` is a mapping from device Curve25519 key to an encrypted
payload for that device. `body` is a Base64-encoded Olm message body.
`type` is an integer indicating the type of the message body: 0 for the
initial pre-key message, 1 for ordinary messages.
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
messages with a type of 1.
When a client receives a message with a type of 0 it must first check if
it already has a matching session. If it does then it will use that
session to try to decrypt the message. If there is no existing session
then the client must create a new session and use the new session to
decrypt the message. A client must not persist a session or remove
one-time keys used by a session until it has successfully decrypted a
message using that session.
Messages with type 1 can only be decrypted with an existing session. If
there is no matching session, the client must treat this as an invalid
message.
The plaintext payload is of the form:
{
"type": "<typeoftheplaintextevent>",
"content": "<contentfortheplaintextevent>",
"sender": "<sender_user_id>",
"recipient": "<recipient_user_id>",
"recipient_keys": {
"ed25519": "<our_ed25519_key>"
},
"keys": {
"ed25519": "<sender_ed25519_key>"
}
}
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` and the `ed25519` field value
under the `keys` property match the keys returned by `/keys/query`\_ for
the given user, and must also verify the signature of the payload.
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
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
of reasons. When this happens to an Olm-encrypted message, the client
should assume that the Olm session has become corrupted and create a new
one to replace it.
Note
Megolm-encrypted messages generally do not have the same problem.
Usually the key for an undecryptable Megolm-encrypted message will come
later, allowing the client to decrypt it successfully. Olm does not have
a way to recover from the failure, making this session replacement
process required.
To establish a new session, the client sends an [m.dummy](#m-dummy)
to-device event to the other party to notify them of the new session
details.
Clients should rate-limit the number of sessions it creates per device
that it receives a message from. Clients should not create a new session
with another device if it has already created one for that given device
in the past 1 hour.
Clients should attempt to mitigate loss of the undecryptable messages.
For example, Megolm sessions that were sent using the old session would
have been lost. The client can attempt to retrieve the lost sessions
through `m.room_key_request` messages.
##### `m.megolm.v1.aes-sha2`
The name `m.megolm.v1.aes-sha2` corresponds to version 1 of the Megolm
ratchet, as defined by the [Megolm
specification](http://matrix.org/docs/spec/megolm.html). This uses:
- HMAC-SHA-256 for the hash ratchet.
- HKDF-SHA-256, AES-256 in CBC mode, and 8 byte truncated HMAC-SHA-256
for authenticated encryption.
- Ed25519 for message authenticity.
Devices that support Megolm must support Olm, and include
"m.megolm.v1.aes-sha2" in their list of supported messaging algorithms.
An event encrypted using Megolm has the following format:
{
"type": "m.room.encrypted",
"content": {
"algorithm": "m.megolm.v1.aes-sha2",
"sender_key": "<sender_curve25519_key>",
"device_id": "<sender_device_id>",
"session_id": "<outbound_group_session_id>",
"ciphertext": "<encrypted_payload_base_64>"
}
}
The encrypted payload can contain any message event. The plaintext is of
the form:
{
"type": "<event_type>",
"content": "<event_content>",
"room_id": "<theroom_id>"
}
We include the room ID in the payload, because otherwise the homeserver
would be able to change the room a message was sent in.
Clients must guard against replay attacks by keeping track of the
ratchet indices of Megolm sessions. They should reject messages with a
ratchet index that they have already decrypted. Care should be taken in
order to avoid false positives, as a client may decrypt the same event
twice as part of its normal processing.
As with Olm events, clients must confirm that the `sender_key` belongs
to the user who sent the message. The same reasoning applies, but the
sender ed25519 key has to be inferred from the `keys.ed25519` property
of the event which established the Megolm session.
In order to enable end-to-end encryption in a room, clients can send an
`m.room.encryption` state event specifying `m.megolm.v1.aes-sha2` as its
`algorithm` property.
When creating a Megolm session in a room, clients must share the
corresponding session key using Olm with the intended recipients, so
that they can decrypt future messages encrypted using this session. An
`m.room_key` event is used to do this. Clients must also handle
`m.room_key` events sent by other devices in order to decrypt their
messages.
#### Protocol definitions
##### Events
{{m\_room\_encryption\_event}}
{{m\_room\_encrypted\_event}}
{{m\_room\_key\_event}}
{{m\_room\_key\_request\_event}}
{{m\_forwarded\_room\_key\_event}}
{{m\_dummy\_event}}
##### Key management API
{{keys\_cs\_http\_api}}
##### Extensions to /sync
This module adds an optional `device_lists` property to the \_ response,
as specified below. The server need only populate this property for an
incremental `/sync` (i.e., one where the `since` parameter was
specified). The client is expected to use `/keys/query`\_ or
`/keys/changes`\_ for the equivalent functionality after an initial
sync, as documented in [Tracking the device list for a
user](#tracking-the-device-list-for-a-user).
It also adds a `one_time_keys_count` property. Note the spelling
difference with the `one_time_key_counts` property in the
`/keys/upload`\_ response.
<table>
<thead>
<trclass="header">
<th>Parameter</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td><p>device_lists</p></td>
<td><p>DeviceLists</p></td>
<td><p>Optional. Information on e2e device updates. Note: only present on an incremental sync.</p></td>
</tr>
<trclass="even">
<td><p>device_one_time_keys_count</p></td>
<td><p>{string: integer}</p></td>
<td><p>Optional. For each key algorithm, the number of unclaimed one-time keys currently held on the server for this device.</p></td>
</tr>
</tbody>
</table>
`DeviceLists`
<table>
<thead>
<trclass="header">
<th>Parameter</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td><p>changed</p></td>
<td><p>[string]</p></td>
<td><p>List of users who have updated their device identity or cross-signing keys, or who now share an encrypted room with the client since the previous sync response.</p></td>
</tr>
<trclass="even">
<td><p>left</p></td>
<td><p>[string]</p></td>
<td><p>List of users with whom we do not share any encrypted rooms anymore since the previous sync response.</p></td>
</tr>
</tbody>
</table>
Note
For optimal performance, Alice should be added to `changed` in Bob's
sync only when she updates her devices or cross-signing keys, or when
Alice and Bob now share a room but didn't share any room previously.
However, for the sake of simpler logic, a server may add Alice to
`changed` when Alice and Bob share a new room, even if they previously
already shared a room.
Example response:
{
"next_batch": "s72595_4483_1934",
"rooms": {"leave": {}, "join": {}, "invite": {}},
"device_lists": {
"changed": [
"@alice:example.com",
],
"left": [
"@bob:example.com",
],
},
"device_one_time_keys_count": {
"curve25519": 10,
"signed_curve25519": 20
}
}
#### Reporting that decryption keys are withheld
When sending an encrypted event to a room, a client can optionally
signal to other devices in that room that it is not sending them the
keys needed to decrypt the event. In this way, the receiving client can
indicate to the user why it cannot decrypt the event, rather than just
showing a generic error message.
In the same way, when one device requests keys from another using [Key
requests](#key-requests), the device from which the key is being
requested may want to tell the requester that it is purposely not
sharing the key.
If Alice withholds a megolm session from Bob for some messages in a
room, and then later on decides to allow Bob to decrypt later messages,
she can send Bob the megolm session, ratcheted up to the point at which
she allows Bob to decrypt the messages. If Bob logs into a new device
and uses key sharing to obtain the decryption keys, the new device will
be sent the megolm sessions that have been ratcheted up. Bob's old
device can include the reason that the session was initially not shared
by including a `withheld` property in the `m.forwarded_room_key` message
that is an object with the `code` and `reason` properties from the