End-to-End Encryption

Matrix optionally supports end-to-end encryption, allowing rooms to be created whose conversation contents are not decryptable or interceptable on any of the participating homeservers.

Key Distribution

Encryption and Authentication in Matrix is based around public-key cryptography. The Matrix protocol provides a basic mechanism for exchange of public keys, though an out-of-band channel is required to exchange fingerprints between users to build a web of trust.

Overview
  1. Bob publishes the public keys and supported algorithms for his device. This may include long-term identity keys, and/or one-time keys.
                                      +----------+  +--------------+
                                      | Bob's HS |  | Bob's Device |
                                      +----------+  +--------------+
                                            |              |
                                            |<=============|
                                              /keys/upload
  1. Alice requests Bob’s public identity keys and supported algorithms.
  +----------------+  +------------+  +----------+
  | Alice's Device |  | Alice's HS |  | Bob's HS |
  +----------------+  +------------+  +----------+
         |                  |               |
         |=================>|==============>|
           /keys/query        <federation>
  1. Alice selects an algorithm and claims any one-time keys needed.
  +----------------+  +------------+  +----------+
  | Alice's Device |  | Alice's HS |  | Bob's HS |
  +----------------+  +------------+  +----------+
         |                  |               |
         |=================>|==============>|
           /keys/claim         <federation>
Key algorithms

The name ed25519 corresponds to the Ed25519 signature algorithm. The key is a 32-byte Ed25519 public key, encoded using unpadded Base64. Example:

"SogYyrkTldLz0BXP+GYWs0qaYacUI0RleEqNT8J3riQ"

The name curve25519 corresponds to the Curve25519 ECDH algorithm. The key is a 32-byte Curve25519 public key, encoded using unpadded Base64. Example:

"JGLn/yafz74HB2AbPLYJWIVGnKAtqECOBf11yyXac2Y"

The name signed_curve25519 also corresponds to the Curve25519 algorithm, but a key using this algorithm is represented by an object with a the following properties:

KeyObject

Parameter Type Description

key

string

Required. The unpadded Base64-encoded 32-byte Curve25519 public key.

signatures

Signatures

Required. Signatures of the key object.

The signature is calculated using the process described at Signing JSON.

Example:

{
  "key":"06UzBknVHFMwgi7AVloY7ylC+xhOhEX4PkNge14Grl8",
  "signatures": {
    "@user:example.com": {
      "ed25519:EGURVBUNJP": "YbJva03ihSj5mPk+CHMJKUKlCXCPFXjXOK6VqBnN9nA2evksQcTGn6hwQfrgRHIDDXO2le49x7jnWJHMJrJoBQ"
    }
  }
}
Device keys

Each device should have one Ed25519 signing key. This key should be generated on the device from a cryptographically secure source, and the private part of the key should never be exported from the device. This key is used as the fingerprint for a device by other clients.

A device will generally need to generate a number of additional keys. Details of these will vary depending on the messaging algorithm in use.

Algorithms generally require device identity keys as well as signing keys. Some algorithms also require one-time keys to improve their secrecy and deniability. These keys are used once during session establishment, and are then thrown away.

For Olm version 1, each device requires a single Curve25519 identity key, and a number of signed Curve25519 one-time keys.

Uploading keys

A device uploads the public parts of identity keys to their homeserver as a signed JSON object, using the /keys/upload API. The JSON object must include the public part of the device’s Ed25519 key, and must be signed by that key, as described in Signing JSON.

One-time keys are also uploaded to the homeserver using the /keys/upload API.

Devices must store the private part of each key they upload. They can discard the private part of a one-time key when they receive a message using that key. However it’s possible that a one-time key given out by a homeserver will never be used, so the device that generates the key will never know that it can discard the key. Therefore a device could end up trying to store too many private keys. A device that is trying to store too many private keys may discard keys starting with the oldest.

Tracking the device list for a user

Before Alice can send an encrypted message to Bob, she needs a list of each of his devices and the associated identity keys, so that she can establish an encryption session with each device. This list can be obtained by calling /keys/query, passing Bob’s user ID in the device_keys parameter.

From time to time, Bob may add new devices, and Alice will need to know this so that she can include his new devices for later encrypted messages. A naive solution to this would be to call /keys/query before sending each message -however, the number of users and devices may be large and this would be inefficient.

It is therefore expected that each client will maintain a list of devices for a number of users (in practice, typically each user with whom we share an encrypted room). Furthermore, it is likely that this list will need to be persisted between invocations of the client application (to preserve device verification data and to alert Alice if Bob suddenly gets a new device).

Alice’s client can maintain a list of Bob’s devices via the following process:

  1. It first sets a flag to record that it is now tracking Bob’s device list, and a separate flag to indicate that its list of Bob’s devices is outdated. Both flags should be in storage which persists over client restarts.
  2. It then makes a request to /keys/query, passing Bob’s user ID in the device_keys parameter. When the request completes, it stores the resulting list of devices in persistent storage, and clears the ‘outdated’ flag.
  3. During its normal processing of responses to _, Alice’s client inspects the changed property of the device_lists field. If it is tracking the device lists of any of the listed users, then it marks the device lists for those users outdated, and initiates another request to /keys/query for them.
  4. Periodically, Alice’s client stores the next_batch field of the result from _ in persistent storage. If Alice later restarts her client, it can obtain a list of the users who have updated their device list while it was offline by calling /keys/changes, passing the recorded next_batch field as the from parameter. If the client is tracking the device list of any of the users listed in the response, it marks them as outdated. It combines this list with those already flagged as outdated, and initiates a /keys/query request for all of them.
Sending encrypted attachments

When encryption is enabled in a room, files should be uploaded encrypted on the homeserver.

In order to achieve this, a client should generate a single-use 256-bit AES key, and encrypt the file using AES-CTR. The counter should be 64-bit long, starting at 0 and prefixed by a random 64-bit Initialization Vector (IV), which together form a 128-bit unique counter block.

Then, the encrypted file can be uploaded to the homeserver. The key and the IV must be included in the room event along with the resulting mxc:// in order to allow recipients to decrypt the file. As the event containing those will be Megolm encrypted, the server will never have access to the decrypted file.

A hash of the ciphertext must also be included, in order to prevent the homeserver from changing the file content.

A client should send the data as an encrypted m.room.message event, using either m.file as the msgtype, or the appropriate msgtype for the file type. The key is sent using the JSON Web Key format, with a W3C extension.

Extensions to m.room.message msgtypes <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

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

Parameter Type Description
url string Required. The URL to the file.
key JWK Required. A JSON Web Key object.

iv

string

Required. The 128-bit unique counter block used by AES-CTR, encoded as unpadded base64.

hashes

{string: string}

Required. 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 sha256.

v

string

Required. Version of the encrypted attachments protocol. Must be v2.

JWK

Parameter Type Description
kty string Required. Key type. Must be oct.

key_ops

[string]

Required. Key operations. Must at least contain encrypt and decrypt.

alg string Required. Algorithm. Must be A256CTR.
k string Required. The key, encoded as urlsafe unpadded base64.

ext

boolean

Required. Extractable. Must be true. This is a W3C extension.

Example:

{
  "content": {
    "body": "something-important.jpg",
    "file": {
      "url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe",
      "mimetype": "image/jpeg",
      "v": "v2",
      "key": {
        "alg": "A256CTR",
        "ext": true,
        "k": "aWF6-32KGYaC3A_FEUCk1Bt0JA37zP0wrStgmdCaW-0",
        "key_ops": ["encrypt","decrypt"],
        "kty": "oct"
      },
      "iv": "w+sE15fzSc0AAAAAAAAAAA",
      "hashes": {
        "sha256": "fdSLu/YkRx3Wyh3KQabP3rd6+SFiKg5lsJZQHtkSAYA"
      }
    },
    "info": {
      "mimetype": "image/jpeg",
      "h": 1536,
      "size": 422018,
      "thumbnail_file": {
        "hashes": {
          "sha256": "/NogKqW5bz/m8xHgFiH5haFGjCNVmUIPLzfvOhHdrxY"
        },
        "iv": "U+k7PfwLr6UAAAAAAAAAAA",
        "key": {
          "alg": "A256CTR",
          "ext": true,
          "k": "RMyd6zhlbifsACM1DXkCbioZ2u0SywGljTH8JmGcylg",
          "key_ops": ["encrypt", "decrypt"],
          "kty": "oct"
        },
        "mimetype": "image/jpeg",
        "url": "mxc://example.org/pmVJxyxGlmxHposwVSlOaEOv",
        "v": "v2"
      },
      "thumbnail_info": {
        "h": 768,
        "mimetype": "image/jpeg",
        "size": 211009,
        "w": 432
      },
      "w": 864
    },
    "msgtype": "m.image"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.message",
  "unsigned": {
      "age": 1234
  }
}
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:

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 a 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 a 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 a m.key.verification.start message to Alice’s device. Upon receipt of this message, Alice’s device should send a 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 indepdently 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 a 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 process.

+---------------+ +---------------+                    +-------------+ +-------------+
| AliceDevice1  | | AliceDevice2  |                    | BobDevice1  | | BobDevice2  |
+---------------+ +---------------+                    +-------------+ +-------------+
        |                 |                                   |               |
        |                 | m.key.verification.request        |               |
        |                 |---------------------------------->|               |
        |                 |                                   |               |
        |                 | m.key.verification.request        |               |
        |                 |-------------------------------------------------->|
        |                 |                                   |               |
        |                 |          m.key.verification.start |               |
        |                 |<----------------------------------|               |
        |                 |                                   |               |
        |                 | m.key.verification.cancel         |               |
        |                 |-------------------------------------------------->|
        |                 |                                   |               |

After the handshake, the verification process begins.

m.key.verification.request


Requests a key verification with another user’s devices. Typically sent as a to-device event.

Event type: Message event

Content

Name Type Description
from_device string Required: The device ID which is initiating the request.
methods [string] Required: The verification methods supported by the sender.
timestamp integer Required: The POSIX timestamp in milliseconds for when the request was made. If the request is in the future by more than 5 minutes or more than 10 minutes in the past, the message should be ignored by the receiver.
transaction_id string Required: An opaque identifier for the verification request. Must be unique with respect to the devices involved.

Examples

{
  "content": {
    "from_device": "AliceDevice2",
    "methods": [
      "m.sas.v1"
    ],
    "timestamp": 1559598944869,
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.request"
}

m.key.verification.start


Begins a key verification process. Typically sent as a to-device event. The method field determines the type of verification. The fields in the event will differ depending on the method. This definition includes fields that are in common among all variants.

Event type: Message event

Content

Name Type Description
from_device string Required: The device ID which is initiating the process.
method string Required: The verification method to use.
next_method string Optional method to use to verify the other user’s key with. Applicable when the method chosen only verifies one user’s key. This field will never be present if the method verifies keys both ways.
transaction_id string Required: An opaque identifier for the verification process. Must be unique with respect to the devices involved. Must be the same as the transaction_id given in the m.key.verification.request if this process is originating from a request.

Examples

{
  "content": {
    "from_device": "BobDevice1",
    "method": "m.sas.v1",
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.start"
}
{
  "content": {
    "from_device": "BobDevice1",
    "hashes": [
      "sha256"
    ],
    "key_agreement_protocols": [
      "curve25519"
    ],
    "message_authentication_codes": [
      "hkdf-hmac-sha256"
    ],
    "method": "m.sas.v1",
    "short_authentication_string": [
      "decimal",
      "emoji"
    ],
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.start"
}

m.key.verification.cancel


Cancels a key verification process/request. Typically sent as a to-device event.

Event type: Message event

Content

Name Type Description
code string Required:

The error code for why the process/request was cancelled by the user. Error codes should use the Java package naming convention if not in the following list:

  • m.user: The user cancelled the verification.

  • m.timeout: The verification process timed out. Verification processes can define their own timeout parameters.

  • m.unknown_transaction: The device does not know about the given transaction ID.

  • m.unknown_method: The device does not know how to handle the requested method. This should be sent for m.key.verification.start messages and messages defined by individual verification processes.

  • m.unexpected_message: The device received an unexpected message. Typically raised when one of the parties is handling the verification out of order.

  • m.key_mismatch: The key was not verified.

  • m.user_mismatch: The expected user did not match the user verified.

  • m.invalid_message: The message received was invalid.

  • m.accepted: A m.key.verification.request was accepted by a different device. The device receiving this error can ignore the verification request.

Clients should be careful to avoid error loops. For example, if a device sends an incorrect message and the client returns m.invalid_message to which it gets an unexpected response with m.unexpected_message, the client should not respond again with m.unexpected_message to avoid the other device potentially sending another error response.

reason string Required: A human readable description of the code. The client should only rely on this string if it does not understand the code.
transaction_id string Required: The opaque identifier for the verification process/request.

Examples

{
  "content": {
    "code": "m.user",
    "reason": "User rejected the key verification request",
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.cancel"
}
Short Authentication String (SAS) verification

SAS verification is a user-friendly key verification process built off the common framework outlined above. SAS verification is intended to be a highly interactive process for users, and as such exposes verfiication methods which are easier for users to use.

The verification process is heavily inspired by Phil Zimmermann’s ZRTP key agreement handshake. A key part of key agreement in ZRTP is the hash commitment: the party that begins the Diffie-Hellman key sharing sends a hash of their part of the Diffie-Hellman exchange, and does not send their part of the Diffie-Hellman exchange until they have received the other party’s part. Thus an attacker essentially only has one attempt to attack the Diffie-Hellman exchange, and hence we can verify fewer bits while still achieving a high degree of security: if we verify n bits, then an attacker has a 1 in 2n chance of success. For example, if we verify 40 bits, then an attacker has a 1 in 1,099,511,627,776 chance (or less than 1 in 1012 chance) of success. A failed attack would result in a mismatched Short Authentication String, alerting users to the attack.

The verification process takes place over to-device messages in two phases:

  1. Key agreement phase (based on ZRTP key agreement).
  2. Key verification phase (based on HMAC).

The process between Alice and Bob verifying each other would be:

  1. Alice and Bob establish a secure out-of-band connection, such as meeting in-person or a video call. “Secure” here means that either party cannot be impersonated, not explicit secrecy.
  2. Alice and Bob communicate which devices they’d like to verify with each other.
  3. Alice selects Bob’s device from the device list and begins verification.
  4. Alice’s client ensures it has a copy of Bob’s device key.
  5. Alice’s device sends Bob’s device a m.key.verification.start message.
  6. Bob’s device receives the message and selects a key agreement protocol, hash algorithm, message authentication code, and SAS method supported by Alice’s device.
  7. Bob’s device ensures it has a copy of Alice’s device key.
  8. Bob’s device creates an ephemeral Curve25519 key pair (KBprivate, KBpubli**c), and calculates the hash (using the chosen algorithm) of the public key KBpubli**c.
  9. Bob’s device replies to Alice’s device with a m.key.verification.accept message.
  10. Alice’s device receives Bob’s message and stores the commitment hash for later use.
  11. Alice’s device creates an ephemeral Curve25519 key pair (KAprivate, KApubli**c) and replies to Bob’s device with a m.key.verification.key, sending only the public key KApubli**c.
  12. Bob’s device receives Alice’s message and replies with its own m.key.verification.key message containing its public key KBpubli**c.
  13. Alice’s device receives Bob’s message and verifies the commitment hash from earlier matches the hash of the key Bob’s device just sent and the content of Alice’s m.key.verification.start message.
  14. Both Alice and Bob’s devices perform an Elliptic-curve Diffie-Hellman (ECD**H(KAprivate, KBpubli**c)), using the result as the shared secret.
  15. Both Alice and Bob’s devices display a SAS to their users, which is derived from the shared key using one of the methods in this section. If multiple SAS methods are available, clients should allow the users to select a method.
  16. Alice and Bob compare the strings shown by their devices, and tell their devices if they match or not.
  17. Assuming they match, Alice and Bob’s devices calculate the HMAC of their own device keys and a comma-separated sorted list of of the key IDs that they wish the other user to verify, using SHA-256 as the hash function. HMAC is defined in RFC 2104. The key for the HMAC is different for each item and is calculated by generating 32 bytes (256 bits) using the key verification HKDF.
  18. Alice’s device sends Bob’s device a m.key.verification.mac message containing the MAC of Alice’s device keys and the MAC of her key IDs to be verified. Bob’s device does the same for Bob’s device keys and key IDs concurrently with Alice.
  19. When the other device receives the m.key.verification.mac message, the device calculates the HMAC of its copies of the other device’s keys given in the message, as well as the HMAC of the comma-separated, sorted, list of key IDs in the message. The device compares these with the HMAC values given in the message, and if everything matches then the device keys are verified.

The wire protocol looks like the following between Alice and Bob’s devices:

+-------------+                    +-----------+
| AliceDevice |                    | BobDevice |
+-------------+                    +-----------+
      |                                 |
      | m.key.verification.start        |
      |-------------------------------->|
      |                                 |
      |       m.key.verification.accept |
      |<--------------------------------|
      |                                 |
      | m.key.verification.key          |
      |-------------------------------->|
      |                                 |
      |          m.key.verification.key |
      |<--------------------------------|
      |                                 |
      | m.key.verification.mac          |
      |-------------------------------->|
      |                                 |
      |          m.key.verification.mac |
      |<--------------------------------|
      |                                 |
Error and exception handling

At any point the interactive verfication can go wrong. The following describes what to do when an error happens:

Verification messages specific to SAS

Building off the common framework, the following events are involved in SAS verification.

The m.key.verification.cancel event is unchanged, however the following error codes are used in addition to those already specified:

m.key.verification.start$m.sas.v1


Begins a SAS key verification process using the m.sas.v1 method. Typically sent as a to-device event.

Event type: Message event

Content

Name Type Description
from_device string Required: The device ID which is initiating the process.
hashes [string] Required: The hash methods the sending device understands. Must include at least sha256.
key_agreement_protocols [string] Required: The key agreement protocols the sending device understands. Should include at least curve25519-hkdf-sha256.
message_authentication_codes [string] Required: The message authentication codes that the sending device understands. Must include at least hkdf-hmac-sha256.
method enum Required: The verification method to use.

One of: [m.sas.v1].

short_authentication_string [string] Required: The SAS methods the sending device (and the sending device’s user) understands. Must include at least decimal. Optionally can include emoji.
transaction_id string Required: An opaque identifier for the verification process. Must be unique with respect to the devices involved. Must be the same as the transaction_id given in the m.key.verification.request if this process is originating from a request.

Examples

m.key.verification.accept


Accepts a previously sent m.key.verification.start message. Typically sent as a to-device event.

Event type: Message event

Content

Name Type Description
commitment string Required: The hash (encoded as unpadded base64) of the concatenation of the device’s ephemeral public key (encoded as unpadded base64) and the canonical JSON representation of the m.key.verification.start message.
hash string Required: The hash method the device is choosing to use, out of the options in the m.key.verification.start message.
key_agreement_protocol string Required: The key agreement protocol the device is choosing to use, out of the options in the m.key.verification.start message.
message_authentication_code string Required: The message authentication code the device is choosing to use, out of the options in the m.key.verification.start message.
short_authentication_string [string] Required: The SAS methods both devices involved in the verification process understand. Must be a subset of the options in the m.key.verification.start message.
transaction_id string Required: An opaque identifier for the verification process. Must be the same as the one used for the m.key.verification.start message.

Examples

{
  "content": {
    "commitment": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg",
    "hash": "sha256",
    "key_agreement_protocol": "curve25519",
    "message_authentication_code": "hkdf-hmac-sha256",
    "method": "m.sas.v1",
    "short_authentication_string": [
      "decimal",
      "emoji"
    ],
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.accept"
}

m.key.verification.key


Sends the ephemeral public key for a device to the partner device. Typically sent as a to-device event.

Event type: Message event

Content

Name Type Description
key string Required: The device’s ephemeral public key, encoded as unpadded base64.
transaction_id string Required: An opaque identifier for the verification process. Must be the same as the one used for the m.key.verification.start message.

Examples

{
  "content": {
    "key": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg",
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.key"
}

m.key.verification.mac


Sends the MAC of a device’s key to the partner device. Typically sent as a to-device event.

Event type: Message event

Content

Name Type Description
keys string Required: The MAC of the comma-separated, sorted, list of key IDs given in the mac property, encoded as unpadded base64.
mac object Required: A map of the key ID to the MAC of the key, using the algorithm in the verification process. The MAC is encoded as unpadded base64.
transaction_id string Required: An opaque identifier for the verification process. Must be the same as the one used for the m.key.verification.start message.

Examples

{
  "content": {
    "keys": "2Wptgo4CwmLo/Y8B8qinxApKaCkBG2fjTWB7AbP5Uy+aIbygsSdLOFzvdDjww8zUVKCmI02eP9xtyJxc/cLiBA",
    "mac": {
      "ed25519:ABCDEF": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg"
    },
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.mac"
}
HKDF calculation

In all of the SAS methods, HKDF is as defined in RFC 5869 and uses the previously agreed-upon hash function for the hash function. The shared secret is supplied as the input keying material. No salt is used. When the key_agreement_protocol is curve25519-hkdf-sha256, the info parameter is the concatenation of:

  • The string MATRIX_KEY_VERIFICATION_SAS|.
  • The Matrix ID of the user who sent the m.key.verification.start message, followed by |.
  • The Device ID of the device which sent the m.key.verification.start message, followed by |.
  • The public key from the m.key.verification.key message sent by the device which sent the m.key.verification.start message, followed by |.
  • The Matrix ID of the user who sent the m.key.verification.accept message, followed by |.
  • The Device ID of the device which sent the m.key.verification.accept message, followed by |.
  • The public key from the m.key.verification.key message sent by the device which sent the m.key.verification.accept message, followed by |.
  • The transaction_id being used.

When the key_agreement_protocol is the deprecated method curve25519, the info parameter is the concatenation of:

  • The string MATRIX_KEY_VERIFICATION_SAS.
  • The Matrix ID of the user who sent the m.key.verification.start message.
  • The Device ID of the device which sent the m.key.verification.start message.
  • The Matrix ID of the user who sent the m.key.verification.accept message.
  • The Device ID of the device which sent the m.key.verification.accept message.
  • The transaction_id being used.

New implementations are discouraged from implementing the curve25519 method.

For verification of each party’s device keys, HKDF is as defined in RFC 5869 and uses SHA-256 as the hash function. The shared secret is supplied as the input keying material. No salt is used, and in the info parameter is the concatenation of:

  • The string MATRIX_KEY_VERIFICATION_MAC.
  • The Matrix ID of the user whose key is being MAC-ed.
  • The Device ID of the device sending the MAC.
  • The Matrix ID of the other user.
  • The Device ID of the device receiving the MAC.
  • The transaction_id being used.
  • The Key ID of the key being MAC-ed, or the string KEY_IDS if the item being MAC-ed is the list of key IDs.
SAS method: decimal

Generate 5 bytes using HKDF then take sequences of 13 bits to convert to decimal numbers (resulting in 3 numbers between 0 and 8191 inclusive each). Add 1000 to each calculated number.

The bitwise operations to get the numbers given the 5 bytes B0, B1, B2, B3, B4 would be:

The digits are displayed to the user either with an appropriate separator, such as dashes, or with the numbers on individual lines.

SAS method: emoji

Generate 6 bytes using HKDF then split the first 42 bits into 7 groups of 6 bits, similar to how one would base64 encode something. Convert each group of 6 bits to a number and use the following table to get the corresponding emoji:

Number Emoji Unicode Description
0 🐶 U+1F436 Dog
1 🐱 U+1F431 Cat
2 🦁 U+1F981 Lion
3 🐎 U+1F40E Horse
4 🦄 U+1F984 Unicorn
5 🐷 U+1F437 Pig
6 🐘 U+1F418 Elephant
7 🐰 U+1F430 Rabbit
8 🐼 U+1F43C Panda
9 🐓 U+1F413 Rooster
10 🐧 U+1F427 Penguin
11 🐢 U+1F422 Turtle
12 🐟 U+1F41F Fish
13 🐙 U+1F419 Octopus
14 🦋 U+1F98B Butterfly
15 🌷 U+1F337 Flower
16 🌳 U+1F333 Tree
17 🌵 U+1F335 Cactus
18 🍄 U+1F344 Mushroom
19 🌏 U+1F30F Globe
20 🌙 U+1F319 Moon
21 ☁️ U+2601U+FE0F Cloud
22 🔥 U+1F525 Fire
23 🍌 U+1F34C Banana
24 🍎 U+1F34E Apple
25 🍓 U+1F353 Strawberry
26 🌽 U+1F33D Corn
27 🍕 U+1F355 Pizza
28 🎂 U+1F382 Cake
29 ❤️ U+2764U+FE0F Heart
30 😀 U+1F600 Smiley
31 🤖 U+1F916 Robot
32 🎩 U+1F3A9 Hat
33 👓 U+1F453 Glasses
34 🔧 U+1F527 Spanner
35 🎅 U+1F385 Santa
36 👍 U+1F44D Thumbs Up
37 ☂️ U+2602U+FE0F Umbrella
38 U+231B Hourglass
39 U+23F0 Clock
40 🎁 U+1F381 Gift
41 💡 U+1F4A1 Light Bulb
42 📕 U+1F4D5 Book
43 ✏️ U+270FU+FE0F Pencil
44 📎 U+1F4CE Paperclip
45 ✂️ U+2702U+FE0F Scissors
46 🔒 U+1F512 Lock
47 🔑 U+1F511 Key
48 🔨 U+1F528 Hammer
49 ☎️ U+260EU+FE0F Telephone
50 🏁 U+1F3C1 Flag
51 🚂 U+1F682 Train
52 🚲 U+1F6B2 Bicycle
53 ✈️ U+2708U+FE0F Aeroplane
54 🚀 U+1F680 Rocket
55 🏆 U+1F3C6 Trophy
56 U+26BD Ball
57 🎸 U+1F3B8 Guitar
58 🎺 U+1F3BA Trumpet
59 🔔 U+1F514 Bell
60 U+2693 Anchor
61 🎧 U+1F3A7 Headphones
62 📁 U+1F4C1 Folder
63 📌 U+1F4CC Pin

Clients SHOULD show the emoji with the descriptions from the table, or appropriate translation of those descriptions. Client authors SHOULD collaborate to create a common set of translations for all languages.

Sharing keys between devices

If Bob has an encrypted conversation with Alice on his computer, and then logs in through his phone for the first time, he may want to have access to the previously exchanged messages. To address this issue, several methods are provided to allow users to transfer keys from one device to another.

Key requests

When a device is missing keys to decrypt messages, it can request the keys by sending m.room_key_request to-device messages to other devices with action set to request.

If a device wishes to share the keys with that device, it can forward the keys to the first device by sending an encrypted m.forwarded_room_key to-device message. The first device should then send an m.room_key_request to-device message with action set to request_cancellation to the other devices that it had originally sent the key request to; a device that receives a request_cancellation should disregard any previously-received request message with the same request_id and requesting_device_id.

If a device does not wish to share keys with that device, it can indicate this by sending an m.room_key.withheld to-device message, as described in Reporting that decryption keys are withheld.

Server-side key backups

Devices may upload encrypted copies of keys to the server. When a device tries to read a message that it does not have keys for, it may request the key from the server and decrypt it. Backups are per-user, and users may replace backups with new backups.

In contrast with Key requests, Server-side key backups do not require another device to be online from which to request keys. However, as the session keys are stored on the server encrypted, it requires users to enter a decryption key to decrypt the session keys.

To create a backup, a client will call POST /_matrix/client/r0/room_keys/version and define how the keys are to be encrypted through the backup’s auth_data; other clients can discover the backup by calling GET /_matrix/client/r0/room_keys/version. Keys are encrypted according to the backup’s auth_data and added to the backup by calling PUT /_matrix/client/r0/room_keys/keys or one of its variants, and can be retrieved by calling GET /_matrix/client/r0/room_keys/keys or one of its variants. Keys can only be written to the most recently created version of the backup. Backups can also be deleted using DELETE /_matrix/client/r0/room_keys/version/{version}, or individual keys can be deleted using DELETE /_matrix/client/r0/room_keys/keys or one of its variants.

Clients must only store keys in backups after they have ensured that the auth_data is trusted, either by checking the signatures on it, or by deriving the public key from a private key that it obtained from a trusted source.

When a client uploads a key for a session that the server already has a key for, the server will choose to either keep the existing key or replace it with the new key based on the key metadata as follows:

Recovery key

If the recovery key (the private half of the backup encryption key) is presented to the user to save, it is presented as a string constructed as follows:

  1. The 256-bit curve25519 private key is prepended by the bytes 0x8B and 0x01
  2. All the bytes in the string above, including the two header bytes, are XORed together to form a parity byte. This parity byte is appended to the byte string.
  3. The byte string is encoded using base58, using the same mapping as is used for Bitcoin addresses, that is, using the alphabet 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz.
  4. A space should be added after every 4th character.

When reading in a recovery key, clients must disregard whitespace, and perform the reverse of steps 1 through 3.

Backup algorithm: m.megolm_backup.v1.curve25519-aes-sha2

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

Parameter Type Description

public_key

string

Required. The curve25519 public key used to encrypt the backups, encoded in unpadded base64.

signatures

Signatures

Optional. Signatures of the auth_data, as Signed JSON

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:

    Parameter Type Description

    algorithm

    string

    Required. The end-to-end message encryption algorithm that the key is for. Must be m.megolm.v1.aes-sha2.

    forwarding_curve25519_key_chain

    [string]

    Required. Chain of Curve25519 keys through which this session was forwarded, via m.forwarded_room_key events.

    sender_key

    string

    Required. Unpadded base64-encoded device curve25519 key.

    sender_claimed_keys

    {string: string}

    Required. A map from algorithm name (ed25519) to the identity key for the sending device.

    session_key

    string

    Required. Unpadded base64-encoded session key in session-sharing format.

  2. Generate an ephemeral curve25519 key, and perform an ECDH with the ephemeral key and the backup’s public key to generate a shared secret. The public half of the ephemeral key, encoded using unpadded base64, becomes the ephemeral property of the session_data.

  3. Using the shared secret, generate 80 bytes by performing an HKDF using SHA-256 as the hash, with a salt of 32 bytes of 0, and with the empty string as the info. The first 32 bytes are used as the AES key, the next 32 bytes are used as the MAC key, and the last 16 bytes are used as the AES initialization vector.

  4. Stringify the JSON object, and encrypt it using AES-CBC-256 with PKCS#7 padding. This encrypted data, encoded using unpadded base64, becomes the ciphertext property of the session_data.

  5. Pass the raw encrypted data (prior to base64 encoding) through HMAC-SHA-256 using the MAC key generated above. The first 8 bytes of the resulting MAC are base64-encoded, and become the mac property of the session_data.

GET /_matrix/client/r0/room_keys/keys


Retrieve the keys from the backup for a given room

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
version string Required: The backup from which to retrieve the keys.

Responses

Status Description
200 The key data. If no keys are found, then an object with an empty rooms property will be returned ({"rooms": {}}).
404 The backup was not found.
429 This request was rate-limited.

200 response

Name Type Description
rooms object A map of room IDs to room key backup data.
{
  "rooms": {
    "!room:example.org": {
      "sessions": {
        "sessionid1": {
          "first_message_index": 1,
          "forwarded_count": 0,
          "is_verified": true,
          "session_data": {
            "ciphertext": "base64+ciphertext+of+JSON+data",
            "ephemeral": "base64+ephemeral+key",
            "mac": "base64+mac+of+ciphertext"
          }
        }
      }
    }
  }
}

404 response

Name Type Description
errcode string Required: An error code.
error string A human-readable error message.
{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version."
}

429 response

Name Type Description
errcode string Required: The M_LIMIT_EXCEEDED error code
error string A human-readable error message.
retry_after_ms integer The amount of time in milliseconds the client should wait before trying the request again.
{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

PUT /_matrix/client/r0/room_keys/keys


Store several keys in the backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
version string Required: The backup in which to store the keys. Must be the current backup.

Request body

Name Type Description
rooms object Required: A map of room IDs to room key backup data.

Request body example

{
  "rooms": {
    "!room:example.org": {
      "sessions": {
        "sessionid1": {
          "first_message_index": 1,
          "forwarded_count": 0,
          "is_verified": true,
          "session_data": {
            "ciphertext": "base64+ciphertext+of+JSON+data",
            "ephemeral": "base64+ephemeral+key",
            "mac": "base64+mac+of+ciphertext"
          }
        }
      }
    }
  }
}

Responses

Status Description
200 The update succeeded
403 The version specified does not match the current backup version. The current version will be included in the current_version field.
404 The backup was not found.
429 This request was rate-limited.

200 response

Name Type Description
count integer Required: The number of keys stored in the backup
etag string Required: The new etag value representing stored keys in the backup. See GET /room_keys/version/{version} for more details.
{
  "count": 10,
  "etag": "abcdefg"
}

403 response

Name Type Description
errcode string Required: An error code.
error string A human-readable error message.
{
  "current_version": "42",
  "errcode": "M_WRONG_ROOM_KEYS_VERSION",
  "error": "Wrong backup version."
}

404 response

Name Type Description
errcode string Required: An error code.
error string A human-readable error message.
{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

Name Type Description
errcode string Required: The M_LIMIT_EXCEEDED error code
error string A human-readable error message.
retry_after_ms integer The amount of time in milliseconds the client should wait before trying the request again.
{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

DELETE /_matrix/client/r0/room_keys/keys


Delete a key from the backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
version string Required: The backup from which to delete the key

Responses

Status Description
200 The update succeeded
404 The backup was not found.
429 This request was rate-limited.

200 response

Name Type Description
count integer Required: The number of keys stored in the backup
etag string Required: The new etag value representing stored keys in the backup. See GET /room_keys/version/{version} for more details.
{
  "count": 10,
  "etag": "abcdefg"
}

404 response

Name Type Description
errcode string Required: An error code.
error string A human-readable error message.
{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

Name Type Description
errcode string Required: The M_LIMIT_EXCEEDED error code
error string A human-readable error message.
retry_after_ms integer The amount of time in milliseconds the client should wait before trying the request again.
{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

GET /_matrix/client/r0/room_keys/keys/{roomId}


Retrieve the keys from the backup for a given room

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string Required: The ID of the room that the requested key is for.
query parameters
Name Type Description
version string Required: The backup from which to retrieve the key.

Responses

Status Description
200 The key data. If no keys are found, then an object with an empty sessions property will be returned ({"sessions": {}}).
404 The backup was not found.
429 This request was rate-limited.

200 response

RoomKeyBackup
Name Type Description
sessions object Required: A map of session IDs to key data.
{
  "sessions": {
    "sessionid1": {
      "first_message_index": 1,
      "forwarded_count": 0,
      "is_verified": true,
      "session_data": {
        "ciphertext": "base64+ciphertext+of+JSON+data",
        "ephemeral": "base64+ephemeral+key",
        "mac": "base64+mac+of+ciphertext"
      }
    }
  }
}

404 response

Name Type Description
errcode string Required: An error code.
error string A human-readable error message.
{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

Name Type Description
errcode string Required: The M_LIMIT_EXCEEDED error code
error string A human-readable error message.
retry_after_ms integer The amount of time in milliseconds the client should wait before trying the request again.
{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

PUT /_matrix/client/r0/room_keys/keys/{roomId}


Store a key in the backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string Required: The ID of the room that the keys are for.
query parameters
Name Type Description
version string Required: The backup in which to store the keys. Must be the current backup.

Request body

RoomKeyBackup
Name Type Description
sessions object Required: A map of session IDs to key data.

Request body example

{
  "sessions": {
    "sessionid1": {
      "first_message_index": 1,
      "forwarded_count": 0,
      "is_verified": true,
      "session_data": {
        "ciphertext": "base64+ciphertext+of+JSON+data",
        "ephemeral": "base64+ephemeral+key",
        "mac": "base64+mac+of+ciphertext"
      }
    }
  }
}

Responses

Status Description
200 The update succeeded
403 The version specified does not match the current backup version. The current version will be included in the current_version field.
404 The backup was not found.
429 This request was rate-limited.

200 response

Name Type Description
count integer Required: The number of keys stored in the backup
etag string Required: The new etag value representing stored keys in the backup. See GET /room_keys/version/{version} for more details.
{
  "count": 10,
  "etag": "abcdefg"
}

403 response

Name Type Description
errcode string Required: An error code.
error string A human-readable error message.
{
  "current_version": "42",
  "errcode": "M_WRONG_ROOM_KEYS_VERSION",
  "error": "Wrong backup version."
}

404 response

Name Type Description
errcode string Required: An error code.
error string A human-readable error message.
{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

Name Type Description
errcode string Required: The M_LIMIT_EXCEEDED error code
error string A human-readable error message.
retry_after_ms integer The amount of time in milliseconds the client should wait before trying the request again.
{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

DELETE /_matrix/client/r0/room_keys/keys/{roomId}


Delete a key from the backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string Required: The ID of the room that the specified key is for.
query parameters
Name Type Description
version string Required: The backup from which to delete the key.

Responses

Status Description
200 The update succeeded
404 The backup was not found.
429 This request was rate-limited.

200 response

Name Type Description
count integer Required: The number of keys stored in the backup
etag string Required: The new etag value representing stored keys in the backup. See GET /room_keys/version/{version} for more details.
{
  "count": 10,
  "etag": "abcdefg"
}

404 response

Name Type Description
errcode string Required: An error code.
error string A human-readable error message.
{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

Name Type Description
errcode string Required: The M_LIMIT_EXCEEDED error code
error string A human-readable error message.
retry_after_ms integer The amount of time in milliseconds the client should wait before trying the request again.
{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

GET /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}


Retrieve a key from the backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string Required: The ID of the room that the requested key is for.
sessionId string Required: The ID of the megolm session whose key is requested.
query parameters
Name Type Description
version string Required: The backup from which to retrieve the key.

Responses

Status Description
200 The key data
404 The key or backup was not found.
429 This request was rate-limited.

200 response

KeyBackupData
Name Type Description
first_message_index integer Required: The index of the first message in the session that the key can decrypt.
forwarded_count integer Required: The number of times this key has been forwarded via key-sharing between devices.
is_verified boolean Required: Whether the device backing up the key verified the device that the key is from.
session_data object Required: Algorithm-dependent data. See the documentation for the backup algorithms in Server-side key backups for more information on the expected format of the data.
{
  "first_message_index": 1,
  "session_data": {
    "ciphertext": "base64+ciphertext+of+JSON+data",
    "ephemeral": "base64+ephemeral+key",
    "mac": "base64+mac+of+ciphertext"
  }
}

404 response

Name Type Description
errcode string Required: An error code.
error string A human-readable error message.
{
  "errcode": "M_NOT_FOUND",
  "error": "Key not found."
}

429 response

Name Type Description
errcode string Required: The M_LIMIT_EXCEEDED error code
error string A human-readable error message.
retry_after_ms integer The amount of time in milliseconds the client should wait before trying the request again.
{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

PUT /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}


Store a key in the backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string Required: The ID of the room that the key is for.
sessionId string Required: The ID of the megolm session that the key is for.
query parameters
Name Type Description
version string Required: The backup in which to store the key. Must be the current backup.

Request body

KeyBackupData
Name Type Description
first_message_index integer Required: The index of the first message in the session that the key can decrypt.
forwarded_count integer Required: The number of times this key has been forwarded via key-sharing between devices.
is_verified boolean Required: Whether the device backing up the key verified the device that the key is from.
session_data object Required: Algorithm-dependent data. See the documentation for the backup algorithms in Server-side key backups for more information on the expected format of the data.

Request body example

{
  "first_message_index": 1,
  "session_data": {
    "ciphertext": "base64+ciphertext+of+JSON+data",
    "ephemeral": "base64+ephemeral+key",
    "mac": "base64+mac+of+ciphertext"
  }
}

Responses

Status Description
200 The update succeeded.
403 The version specified does not match the current backup version. The current version will be included in the current_version field.
429 This request was rate-limited.

200 response

Name Type Description
count integer Required: The number of keys stored in the backup
etag string Required: The new etag value representing stored keys in the backup. See GET /room_keys/version/{version} for more details.
{
  "count": 10,
  "etag": "abcdefg"
}

403 response

Name Type Description
errcode string Required: An error code.
error string A human-readable error message.
{
  "current_version": "42",
  "errcode": "M_WRONG_ROOM_KEYS_VERSION",
  "error": "Wrong backup version."
}

429 response

Name Type Description
errcode string Required: The M_LIMIT_EXCEEDED error code
error string A human-readable error message.
retry_after_ms integer The amount of time in milliseconds the client should wait before trying the request again.
{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

DELETE /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}


Delete a key from the backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string Required: The ID of the room that the specified key is for.
sessionId string Required: The ID of the megolm session whose key is to be deleted.
query parameters
Name Type Description
version string Required: The backup from which to delete the key

Responses

Status Description
200 The update succeeded
404 The backup was not found.
429 This request was rate-limited.

200 response

Name Type Description
count integer Required: The number of keys stored in the backup
etag string Required: The new etag value representing stored keys in the backup. See GET /room_keys/version/{version} for more details.
{
  "count": 10,
  "etag": "abcdefg"
}

404 response

Name Type Description
errcode string Required: An error code.
error string A human-readable error message.
{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

Name Type Description
errcode string Required: The M_LIMIT_EXCEEDED error code
error string A human-readable error message.
retry_after_ms integer The amount of time in milliseconds the client should wait before trying the request again.
{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

GET /_matrix/client/r0/room_keys/version


Get information about the latest backup version.

Rate-limited: Yes
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 The information about the backup.
404 No backup exists.
429 This request was rate-limited.

200 response

Name Type Description
algorithm enum Required: The algorithm used for storing backups.

One of: [m.megolm_backup.v1.curve25519-aes-sha2].

auth_data object Required: Algorithm-dependent data. See the documentation for the backup algorithms in Server-side key backups for more information on the expected format of the data.
count integer Required: The number of keys stored in the backup.
etag string Required: An opaque string representing stored keys in the backup. Clients can compare it with the etag value they received in the request of their last key storage request. If not equal, another client has modified the backup.
version string Required: The backup version.
{
  "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
  "auth_data": {
    "public_key": "abcdefg",
    "signatures": {
      "@alice:example.org": {
        "ed25519:deviceid": "signature"
      }
    }
  },
  "count": 42,
  "etag": "anopaquestring",
  "version": "1"
}

404 response

Name Type Description
errcode string Required: An error code.
error string A human-readable error message.
{
  "errcode": "M_NOT_FOUND",
  "error": "No current backup version"
}

429 response

Name Type Description
errcode string Required: The M_LIMIT_EXCEEDED error code
error string A human-readable error message.
retry_after_ms integer The amount of time in milliseconds the client should wait before trying the request again.
{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

POST /_matrix/client/r0/room_keys/version


Creates a new backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request body

Name Type Description
algorithm enum Required: The algorithm used for storing backups.

One of: [m.megolm_backup.v1.curve25519-aes-sha2].

auth_data object Required: Algorithm-dependent data. See the documentation for the backup algorithms in Server-side key backups for more information on the expected format of the data.

Request body example

{
  "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
  "auth_data": {
    "public_key": "abcdefg",
    "signatures": {
      "@alice:example.org": {
        "ed25519:deviceid": "signature"
      }
    }
  }
}

Responses

Status Description
200 The version id of the new backup.
429 This request was rate-limited.

200 response

Name Type Description
version string Required: The backup version. This is an opaque string.
{
  "version": "1"
}

429 response

Name Type Description
errcode string Required: The M_LIMIT_EXCEEDED error code
error string A human-readable error message.
retry_after_ms integer The amount of time in milliseconds the client should wait before trying the request again.
{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

GET /_matrix/client/r0/room_keys/version/{version}


Get information about an existing backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
version string Required: The backup version to get, as returned in the version parameter of the response in POST /_matrix/client/r0/room_keys/version or this endpoint.

Responses

Status Description
200 The information about the requested backup.
404 The backup specified does not exist.
429 This request was rate-limited.

200 response

Name Type Description
algorithm enum Required: The algorithm used for storing backups.

One of: [m.megolm_backup.v1.curve25519-aes-sha2].

auth_data object Required: Algorithm-dependent data. See the documentation for the backup algorithms in Server-side key backups for more information on the expected format of the data.
count integer Required: The number of keys stored in the backup.
etag string Required: An opaque string representing stored keys in the backup. Clients can compare it with the etag value they received in the request of their last key storage request. If not equal, another client has modified the backup.
version string Required: The backup version.
{
  "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
  "auth_data": {
    "public_key": "abcdefg",
    "signatures": {
      "@alice:example.org": {
        "ed25519:deviceid": "signature"
      }
    }
  },
  "count": 42,
  "etag": "anopaquestring",
  "version": "1"
}

404 response

Name Type Description
errcode string Required: An error code.
error string A human-readable error message.
{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

Name Type Description
errcode string Required: The M_LIMIT_EXCEEDED error code
error string A human-readable error message.
retry_after_ms integer The amount of time in milliseconds the client should wait before trying the request again.
{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

PUT /_matrix/client/r0/room_keys/version/{version}


Update information about an existing backup. Only auth_data can be modified.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
version string Required: The backup version to update, as returned in the version parameter in the response of POST /_matrix/client/r0/room_keys/version or GET /_matrix/client/r0/room_keys/version/{version}.

Request body

Name Type Description
algorithm enum Required: The algorithm used for storing backups. Must be the same as the algorithm currently used by the backup.

One of: [m.megolm_backup.v1.curve25519-aes-sha2].

auth_data object Required: Algorithm-dependent data. See the documentation for the backup algorithms in Server-side key backups for more information on the expected format of the data.
version string The backup version. If present, must be the same as the version in the path parameter.

Request body example

{
  "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
  "auth_data": {
    "public_key": "abcdefg",
    "signatures": {
      "@alice:example.org": {
        "ed25519:deviceid": "signature"
      }
    }
  },
  "version": "1"
}

Responses

Status Description
200 The update succeeded.
400 A parameter was incorrect. For example, the algorithm does not match the current backup algorithm, or the version in the body does not match the version in the path.
404 The backup specified does not exist.
429 This request was rate-limited.

400 response

Name Type Description
errcode string Required: An error code.
error string A human-readable error message.
{
  "errcode": "M_INVALID_PARAM",
  "error": "Algorithm does not match"
}

404 response

Name Type Description
errcode string Required: An error code.
error string A human-readable error message.
{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

Name Type Description
errcode string Required: The M_LIMIT_EXCEEDED error code
error string A human-readable error message.
retry_after_ms integer The amount of time in milliseconds the client should wait before trying the request again.
{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

DELETE /_matrix/client/r0/room_keys/version/{version}


Delete an existing key backup. Both the information about the backup, as well as all key data related to the backup will be deleted.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
version string Required: The backup version to delete, as returned in the version parameter in the response of POST /_matrix/client/r0/room_keys/version or GET /_matrix/client/r0/room_keys/version/{version}.

Responses

Status Description
200 The delete succeeded, or the specified backup was previously deleted.
404 The backup specified does not exist. If the backup was previously deleted, the call should succeed rather than returning an error.
429 This request was rate-limited.

404 response

Name Type Description
errcode string Required: An error code.
error string A human-readable error message.
{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

Name Type Description
errcode string Required: The M_LIMIT_EXCEEDED error code
error string A human-readable error message.
retry_after_ms integer The amount of time in milliseconds the client should wait before trying the request again.
{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}
Key exports

Keys can be manually exported from one device to an encrypted file, copied to another device, and imported. The file is encrypted using a user-supplied passphrase, and is created as follows:

  1. Encode the sessions as a JSON object, formatted as described in Key export format.

  2. Generate a 512-bit key from the user-entered passphrase by computing PBKDF2(HMAC-SHA-512, passphrase, S, N, 512), where S is a 128-bit cryptographically-random salt and N is the number of rounds. N should be at least 100,000. The keys K and K' are set to the first and last 256 bits of this generated key, respectively. K is used as an AES-256 key, and K' is used as an HMAC-SHA-256 key.

  3. Serialize the JSON object as a UTF-8 string, and encrypt it using AES-CTR-256 with the key K generated above, and with a 128-bit cryptographically-random initialization vector, IV, that has bit 63 set to zero. (Setting bit 63 to zero in IV is needed to work around differences in implementations of AES-CTR.)

  4. Concatenate the following data:

    Size (bytes) Description
    1 Export format version, which must be 0x01.
    16 The salt S.
    16 The initialization vector IV.
    4 The number of rounds N, as a big-endian unsigned 32-bit integer.
    variable The encrypted JSON object.

    32

    The HMAC-SHA-256 of all the above string concatenated together, using K' as the key.

  5. Base64-encode the string above. Newlines may be added to avoid overly long lines.

  6. Prepend the resulting string with -----BEGIN MEGOLM SESSION DATA-----, with a trailing newline, and append -----END MEGOLM SESSION DATA-----, with a leading and trailing newline.

Key export format

The exported sessions are formatted as a JSON array of SessionData objects described as follows:

SessionData

Parameter Type Description

algorithm

string

Required. The encryption algorithm that the session uses. Must be m.megolm.v1.aes-sha2.

forwarding_curve25519_key_chain

[string]

Required. Chain of Curve25519 keys through which this session was forwarded, via m.forwarded_room_key events.

room_id

string

Required. The room where the session is used.

sender_key

string

Required. The Curve25519 key of the device which initiated the session originally.

sender_claimed_keys

{string: string}

Required. The Ed25519 key of the device which initiated the session originally.

session_id string Required. The ID of the session.
session_key string Required. The key for the session.

This is similar to the format before encryption used for the session keys in Server-side key backups but adds the room_id and session_id fields.

Example:

[
    {
        "algorithm": "m.megolm.v1.aes-sha2",
        "forwarding_curve25519_key_chain": [
            "hPQNcabIABgGnx3/ACv/jmMmiQHoeFfuLB17tzWp6Hw"
        ],
        "room_id": "!Cuyf34gef24t:localhost",
        "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
        "sender_claimed_keys": {
            "ed25519": "<device ed25519 identity key>",
        },
        "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ",
        "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8Llf..."
    },
    ...
]

Messaging Algorithms

Messaging Algorithm Names

Messaging algorithm names use the extensible naming scheme used throughout this specification. Algorithm names that start with m. are reserved for algorithms defined by this specification. Implementations wanting to experiment with new algorithms must be uniquely globally namespaced following Java’s package naming conventions.

Algorithm names should be short and meaningful, and should list the primitives used by the algorithm so that it is easier to see if the algorithm is using a broken primitive.

A name of m.olm.v1 is too short: it gives no information about the primitives in use, and is difficult to extend for different primitives. However a name of m.olm.v1.ecdh-curve25519-hdkfsha256.hmacsha256.hkdfsha256-aes256-cbc-hmac64sha256 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. This uses:

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

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.

To establish a new session, the client sends a 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. This uses:

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": "<the room_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 a 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. A 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


Defines how messages sent in this room should be encrypted.

Event type: State event
State key A zero-length string.

Content

Name Type Description
algorithm enum Required: The encryption algorithm to be used to encrypt messages sent in this room.

One of: [m.megolm.v1.aes-sha2].

rotation_period_ms integer How long the session should be used before changing it. 604800000 (a week) is the recommended default.
rotation_period_msgs integer How many messages should be sent before changing the session. 100 is the recommended default.

Examples

{
  "content": {
    "algorithm": "m.megolm.v1.aes-sha2",
    "rotation_period_ms": 604800000,
    "rotation_period_msgs": 100
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.encryption",
  "unsigned": {
    "age": 1234
  }
}

m.room.encrypted


This event type is used when sending encrypted events. It can be used either within a room (in which case it will have all of the Room Event fields, or as a to-device event.

Event type: Message event

Content

Name Type Description
algorithm enum Required: The encryption algorithm used to encrypt this event. The value of this field determines which other properties will be present.

One of: [m.olm.v1.curve25519-aes-sha2 m.megolm.v1.aes-sha2].

ciphertext Required: The encrypted content of the event. Either the encrypted payload itself, in the case of a Megolm event, or a map from the recipient Curve25519 identity key to ciphertext information, in the case of an Olm event. For more details, see Messaging Algorithms.
device_id string The ID of the sending device. Required with Megolm.
sender_key string Required: The Curve25519 key of the sender.
session_id string The ID of the session used to encrypt the message. Required with Megolm.

Examples

{
  "content": {
    "algorithm": "m.megolm.v1.aes-sha2",
    "ciphertext": "AwgAEnACgAkLmt6qF84IK++J7UDH2Za1YVchHyprqTqsg...",
    "device_id": "RJYKSTBOIE",
    "sender_key": "IlRMeOPX2e0MurIyfWEucYBRVOEEUMrOHqn/8mLqMjA",
    "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.encrypted",
  "unsigned": {
    "age": 1234
  }
}
{
  "content": {
    "algorithm": "m.olm.v1.curve25519-aes-sha2",
    "ciphertext": {
      "7qZcfnBmbEGzxxaWfBjElJuvn7BZx+lSz/SvFrDF/z8": {
        "body": "AwogGJJzMhf/S3GQFXAOrCZ3iKyGU5ZScVtjI0KypTYrW...",
        "type": 0
      }
    },
    "sender_key": "Szl29ksW/L8yZGWAX+8dY1XyFi+i5wm+DRhTGkbMiwU"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.encrypted",
  "unsigned": {
    "age": 1234
  }
}

m.room_key


This event type is used to exchange keys for end-to-end encryption. Typically it is encrypted as an m.room.encrypted event, then sent as a to-device event.

Event type: Message event

Content

Name Type Description
algorithm enum Required: The encryption algorithm the key in this event is to be used with.

One of: [m.megolm.v1.aes-sha2].

room_id string Required: The room where the key is used.
session_id string Required: The ID of the session that the key is for.
session_key string Required: The key to be exchanged.

Examples

{
  "content": {
    "algorithm": "m.megolm.v1.aes-sha2",
    "room_id": "!Cuyf34gef24t:localhost",
    "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ",
    "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8LlfJL7qNBEY..."
  },
  "type": "m.room_key"
}

m.room_key_request


This event type is used to request keys for end-to-end encryption. It is sent as an unencrypted to-device event.

Event type: Message event

Content

Name Type Description
action enum Required:

One of: [request request_cancellation].

body RequestedKeyInfo Information about the requested key. Required when action is request.
request_id string Required: A random string uniquely identifying the request for a key. If the key is requested multiple times, it should be reused. It should also reused in order to cancel a request.
requesting_device_id string Required: ID of the device requesting the key.
RequestedKeyInfo
Name Type Description
algorithm string Required: The encryption algorithm the requested key in this event is to be used with.
room_id string Required: The room where the key is used.
sender_key string Required: The Curve25519 key of the device which initiated the session originally.
session_id string Required: The ID of the session that the key is for.

Examples

{
  "content": {
    "action": "request_cancellation",
    "request_id": "1495474790150.19",
    "requesting_device_id": "RJYKSTBOIE"
  },
  "type": "m.room_key_request"
}
{
  "content": {
    "action": "request",
    "body": {
      "algorithm": "m.megolm.v1.aes-sha2",
      "room_id": "!Cuyf34gef24t:localhost",
      "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
      "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ"
    },
    "request_id": "1495474790150.19",
    "requesting_device_id": "RJYKSTBOIE"
  },
  "type": "m.room_key_request"
}

m.forwarded_room_key


This event type is used to forward keys for end-to-end encryption. Typically it is encrypted as an m.room.encrypted event, then sent as a to-device event.

Event type: Message event

Content

Name Type Description
algorithm string Required: The encryption algorithm the key in this event is to be used with.
forwarding_curve25519_key_chain [string] Required: Chain of Curve25519 keys. It starts out empty, but each time the key is forwarded to another device, the previous sender in the chain is added to the end of the list. For example, if the key is forwarded from A to B to C, this field is empty between A and B, and contains A’s Curve25519 key between B and C.
room_id string Required: The room where the key is used.
sender_claimed_ed25519_key string Required: The Ed25519 key of the device which initiated the session originally. It is ‘claimed’ because the receiving device has no way to tell that the original room_key actually came from a device which owns the private part of this key unless they have done device verification.
sender_key string Required: The Curve25519 key of the device which initiated the session originally.
session_id string Required: The ID of the session that the key is for.
session_key string Required: The key to be exchanged.
withheld object Indicates that the key cannot be used to decrypt all the messages from the session because a portion of the session was withheld as described in Reporting that decryption keys are withheld. This object must include the code and reason properties from the m.room_key.withheld message that was received by the sender of this message.

Examples

{
  "content": {
    "algorithm": "m.megolm.v1.aes-sha2",
    "forwarding_curve25519_key_chain": [
      "hPQNcabIABgGnx3/ACv/jmMmiQHoeFfuLB17tzWp6Hw"
    ],
    "room_id": "!Cuyf34gef24t:localhost",
    "sender_claimed_ed25519_key": "aj40p+aw64yPIdsxoog8jhPu9i7l7NcFRecuOQblE3Y",
    "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
    "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ",
    "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8Llf..."
  },
  "type": "m.forwarded_room_key"
}

m.dummy


This event type is used to indicate new Olm sessions for end-to-end encryption. Typically it is encrypted as an m.room.encrypted event, then sent as a to-device event.

The event does not have any content associated with it. The sending client is expected to send a key share request shortly after this message, causing the receiving client to process this m.dummy event as the most recent event and using the keyshare request to set up the session. The keyshare request and m.dummy combination should result in the original sending client receiving keys over the newly established session.

Event type: Message event

Content

Examples

{
  "content": {},
  "type": "m.dummy"
}
Key management API

GET /_matrix/client/r0/keys/changes


Gets a list of users who have updated their device identity keys since a previous sync token.

The server should include in the results any users who:

  • currently share a room with the calling user (ie, both users have membership state join); and
  • added new device identity keys or removed an existing device with identity keys, between from and to.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
from string Required: The desired start point of the list. Should be the next_batch field from a response to an earlier call to /sync. Users who have not uploaded new device identity keys since this point, nor deleted existing devices with identity keys since then, will be excluded from the results.
to string Required: The desired end point of the list. Should be the next_batch field from a recent call to /sync - typically the most recent such call. This may be used by the server as a hint to check its caches are up to date.

Responses

Status Description
200 The list of users who updated their devices.

200 response

Name Type Description
changed [string] The Matrix User IDs of all users who updated their device identity keys.
left [string] The Matrix User IDs of all users who may have left all the end-to-end encrypted rooms they previously shared with the user.
{
  "changed": [
    "@alice:example.com",
    "@bob:example.org"
  ],
  "left": [
    "@clara:example.com",
    "@doug:example.org"
  ]
}

POST /_matrix/client/r0/keys/claim


Claims one-time keys for use in pre-key messages.

Rate-limited: No
Requires authentication: Yes

Request

Request body

Name Type Description
one_time_keys object Required: The keys to be claimed. A map from user ID, to a map from device ID to algorithm name.
timeout integer The time (in milliseconds) to wait when downloading keys from remote servers. 10 seconds is the recommended default.

Request body example

{
  "one_time_keys": {
    "@alice:example.com": {
      "JLAFKJWSCS": "signed_curve25519"
    }
  },
  "timeout": 10000
}

Responses

Status Description
200 The claimed keys.

200 response

Name Type Description
failures object

If any remote homeservers could not be reached, they are recorded here. The names of the properties are the names of the unreachable servers.

If the homeserver could be reached, but the user or device was unknown, no failure is recorded. Instead, the corresponding user or device is missing from the one_time_keys result.

one_time_keys object Required:

One-time keys for the queried devices. A map from user ID, to a map from devices to a map from <algorithm>:<key_id> to the key object.

See the key algorithm section for information on the Key Object format.

{
  "one_time_keys": {
    "@alice:example.com": {
      "JLAFKJWSCS": {
        "signed_curve25519:AAAAHg": {
          "key": "zKbLg+NrIjpnagy+pIY6uPL4ZwEG2v+8F9lmgsnlZzs",
          "signatures": {
            "@alice:example.com": {
              "ed25519:JLAFKJWSCS": "FLWxXqGbwrb8SM3Y795eB6OA8bwBcoMZFXBqnTn58AYWZSqiD45tlBVcDa2L7RwdKXebW/VzDlnfVJ+9jok1Bw"
            }
          }
        }
      }
    }
  }
}

POST /_matrix/client/r0/keys/query


Returns the current devices and identity keys for the given users.

Rate-limited: No
Requires authentication: Yes

Request

Request body

Name Type Description
device_keys object Required: The keys to be downloaded. A map from user ID, to a list of device IDs, or to an empty list to indicate all devices for the corresponding user.
timeout integer The time (in milliseconds) to wait when downloading keys from remote servers. 10 seconds is the recommended default.
token string If the client is fetching keys as a result of a device update received in a sync request, this should be the ‘since’ token of that sync request, or any later sync token. This allows the server to ensure its response contains the keys advertised by the notification in that sync.

Request body example

{
  "device_keys": {
    "@alice:example.com": []
  },
  "timeout": 10000
}

Responses

Status Description
200 The device information

200 response

Name Type Description
device_keys object Information on the queried devices. A map from user ID, to a map from device ID to device information. For each device, the information returned will be the same as uploaded via /keys/upload, with the addition of an unsigned property.
failures object

If any remote homeservers could not be reached, they are recorded here. The names of the properties are the names of the unreachable servers.

If the homeserver could be reached, but the user or device was unknown, no failure is recorded. Instead, the corresponding user or device is missing from the device_keys result.

{
  "device_keys": {
    "@alice:example.com": {
      "JLAFKJWSCS": {
        "algorithms": [
          "m.olm.v1.curve25519-aes-sha2",
          "m.megolm.v1.aes-sha2"
        ],
        "device_id": "JLAFKJWSCS",
        "keys": {
          "curve25519:JLAFKJWSCS": "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI",
          "ed25519:JLAFKJWSCS": "lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI"
        },
        "signatures": {
          "@alice:example.com": {
            "ed25519:JLAFKJWSCS": "dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA"
          }
        },
        "unsigned": {
          "device_display_name": "Alice's mobile phone"
        },
        "user_id": "@alice:example.com"
      }
    }
  }
}

POST /_matrix/client/r0/keys/upload


Publishes end-to-end encryption keys for the device.

Rate-limited: No
Requires authentication: Yes

Request

Request body

Name Type Description
device_keys DeviceKeys Identity keys for the device. May be absent if no new identity keys are required.
one_time_keys object

One-time public keys for “pre-key” messages. The names of the properties should be in the format <algorithm>:<key_id>. The format of the key is determined by the key algorithm.

May be absent if no new one-time keys are required.

DeviceKeys
Name Type Description
algorithms [string] Required: The encryption algorithms supported by this device.
device_id string Required: The ID of the device these keys belong to. Must match the device ID used when logging in.
keys object Required: Public identity keys. The names of the properties should be in the format <algorithm>:<device_id>. The keys themselves should be encoded as specified by the key algorithm.
signatures Signatures Required:

Signatures for the device key object. A map from user ID, to a map from <algorithm>:<device_id> to the signature.

The signature is calculated using the process described at Signing JSON.

user_id string Required: The ID of the user the device belongs to. Must match the user ID used when logging in.

Request body example

{
  "device_keys": {
    "algorithms": [
      "m.olm.v1.curve25519-aes-sha2",
      "m.megolm.v1.aes-sha2"
    ],
    "device_id": "JLAFKJWSCS",
    "keys": {
      "curve25519:JLAFKJWSCS": "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI",
      "ed25519:JLAFKJWSCS": "lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI"
    },
    "signatures": {
      "@alice:example.com": {
        "ed25519:JLAFKJWSCS": "dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA"
      }
    },
    "user_id": "@alice:example.com"
  },
  "one_time_keys": {
    "curve25519:AAAAAQ": "/qyvZvwjiTxGdGU0RCguDCLeR+nmsb3FfNG3/Ve4vU8",
    "signed_curve25519:AAAAHQ": {
      "key": "j3fR3HemM16M7CWhoI4Sk5ZsdmdfQHsKL1xuSft6MSw",
      "signatures": {
        "@alice:example.com": {
          "ed25519:JLAFKJWSCS": "IQeCEPb9HFk217cU9kw9EOiusC6kMIkoIRnbnfOh5Oc63S1ghgyjShBGpu34blQomoalCyXWyhaaT3MrLZYQAA"
        }
      }
    },
    "signed_curve25519:AAAAHg": {
      "key": "zKbLg+NrIjpnagy+pIY6uPL4ZwEG2v+8F9lmgsnlZzs",
      "signatures": {
        "@alice:example.com": {
          "ed25519:JLAFKJWSCS": "FLWxXqGbwrb8SM3Y795eB6OA8bwBcoMZFXBqnTn58AYWZSqiD45tlBVcDa2L7RwdKXebW/VzDlnfVJ+9jok1Bw"
        }
      }
    }
  }
}

Responses

Status Description
200 The provided keys were successfully uploaded.

200 response

Name Type Description
one_time_key_counts object Required: For each key algorithm, the number of unclaimed one-time keys of that type currently held on the server for this device.
{
  "one_time_key_counts": {
    "curve25519": 10,
    "signed_curve25519": 20
  }
}
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 (ie, 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.

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.

Parameter Type Description

device_lists

DeviceLists

Optional. Information on e2e device updates. Note: only present on an incremental sync.

device_one_time_keys_count

{string: integer}

Optional. For each key algorithm, the number of unclaimed one-time keys currently held on the server for this device.

DeviceLists

Parameter Type Description

changed

[string]

List of users who have updated their device identity keys, or who now share an encrypted room with the client since the previous sync response.

left

[string]

List of users with whom we do not share any encrypted rooms anymore since the previous sync response.

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, 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 m.room_key.withheld message.

m.room_key.withheld


This event type is used to indicate that the sender is not sharing room keys with the recipient. It is sent as a to-device event.

Possible values for code include:

  • m.blacklisted: the user/device was blacklisted.
  • m.unverified: the user/device was not verified, and the sender is only sharing keys with verified users/devices.
  • m.unauthorised: the user/device is not allowed to have the key. For example, this could be sent in response to a key request if the user/device was not in the room when the original message was sent.
  • m.unavailable: sent in reply to a key request if the device that the key is requested from does not have the requested key.
  • m.no_olm: an olm session could not be established.

In most cases, this event refers to a specific room key. The one exception to this is when the sender is unable to establish an olm session with the recipient. When this happens, multiple sessions will be affected. In order to avoid filling the recipient's device mailbox, the sender should only send one m.room_key.withheld message with no room_id nor session_id set. If the sender retries and fails to create an olm session again in the future, it should not send another m.room_key.withheld message with a code of m.no_olm, unless another olm session was previously established successfully. In response to receiving an m.room_key.withheld message with a code of m.no_olm, the recipient may start an olm session with the sender and send an m.dummy message to notify the sender of the new olm session. The recipient may assume that this m.room_key.withheld message applies to all encrypted room messages sent before it receives the message.

Event type: Message event

Content

Name Type Description
algorithm enum Required: The encryption algorithm for the key that this event is about.

One of: [m.megolm.v1.aes-sha2].

code enum Required: A machine-readable code for why the key was not sent. Codes beginning with m. are reserved for codes defined in the Matrix specification. Custom codes must use the Java package naming convention.

One of: [m.blacklisted m.unverified m.unauthorised m.unavailable m.no_olm].

reason string A human-readable reason for why the key was not sent. The receiving client should only use this string if it does not understand the code.
room_id string Required if code is not m.no_olm. The room for the key that this event is about.
sender_key string Required: The unpadded base64-encoded device curve25519 key of the event's sender.
session_id string Required of code is not m.no_olm. The session ID of the key that this event is aboutis for.

Examples

{
  "content": {
    "algorithm": "m.megolm.v1.aes-sha2",
    "code": "m.unverified",
    "reason": "Device not verified",
    "room_id": "!Cuyf34gef24t:localhost",
    "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
    "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ"
  },
  "type": "m.room_key.withheld"
}