Expand description
End-to-end encryption related types
Matrix has support for end-to-end encrypted messaging. This module contains
types related to end-to-end encryption, describes a bit how E2EE works in
the matrix-sdk, and how to set your Client
up to support E2EE.
Jump to the Client Setup section if you don’t care how E2EE works under the hood.
§End-to-end encryption
While all messages in Matrix land are transferred to the server in an
encrypted manner, rooms can be marked as end-to-end encrypted. If a room is
marked as end-to-end encrypted, using a m.room.encrypted
state event, all
messages that are sent to this room will be encrypted for the individual
room members. This means that the server won’t be able to read messages that
get sent to such a room.
┌──────────────┐
│ Homeserver │
┌───────┐ │ │ ┌───────┐
│ Alice │═══════════════►│ unencrypted │═══════════════►│ Bob │
└───────┘ encrypted │ │ encrypted └───────┘
└──────────────┘
┌──────────────┐
│ Homeserver │
┌───────┐ │ │ ┌───────┐
│ Alice │≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡►│─────────────►│≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡►│ Bob │
└───────┘ encrypted │ encrypted │ encrypted └───────┘
└──────────────┘
§Encrypting for each end
We already mentioned that a message in a end-to-end encrypted world needs to be encrypted for each individual member, though that isn’t completely correct. A message needs to be encrypted for each individual end. An end in Matrix land is a client that communicates with the homeserver. The spec calls an end a Device, while other clients might call an end a Session.
The matrix-sdk represents an end as a Device
object. Each individual
message should be encrypted for each individual Device
of each
individual room member.
Since rooms might grow quite big, encrypting each message for every
Device
becomes quickly unsustainable. Because of that room keys have
been introduced.
§Room keys
Room keys remove the need to encrypt each message for each end. Instead a room key needs to be shared with each end, and after that a message can be encrypted in a single, O(1), step.
A room key is backed by a Megolm session, which consists of two parts. The first part, the outbound group session, is used for encryption. This part never leaves your device. The second part is the inbound group session, which is shared with each end.
┌────────────────────────┬───────────────────────┐
│ Encryption │ Decryption │
├────────────────────────┼───────────────────────┤
│ Outbound group session │ Inbound group session │
└────────────────────────┴───────────────────────┘
§Lifetime of a room key
- Create a room key
- Share the room key with each participant
- Encrypt messages using the room key
- If needed, rotate the room key and go back to 1
The m.room.encryption
state event of the room decides how long a room key
should be used. By default this is for 100 messages or for 1 week, whichever
comes first.
§Decrypting the room history
Since room keys get relatively often rotated, each room key will need to be stored — otherwise we won’t be able to decrypt historical messages. The SDK stores all room keys locally in an encrypted manner.
Besides storing them as part of the SDK store, users can export room keys
using the Encryption::export_room_keys
method.
§Verification
One important aspect of end-to-end encryption is to check that the end you are communicating with is indeed the person you expect. This checking is done in Matrix via interactive verification. While interactively verifying, we’ll need to exchange some critical piece of information over another communication channel. (Good ways to make this exchange would be in person or via a phone call.)
Usually each end will need to verify every end it communicates with. An
end is represented as a Device
in the matrix-sdk. This gets rather
complicated quickly as is shown bellow, with Alice and Bob each having two
devices. Each arrow represents who needs to verify whom for the
communication between Alice and Bob to be considered secure.
┌───────────────────────────────────────────┐
▼ │
┌───────────┐ ┌────┴────┐
┌►│Alice Phone├───────────────────────────────►│Bob Phone│◄──┐
│ └─────┬─────┘ └─────┬───┘ │
│ ▼ ▼ │
│ ┌────────────┐ ┌───────────┐ │
└─┤Alice Laptop├──────────────────────────────►│Bob Desktop├─┘
└────────────┘ └─────┬─────┘
▲ │
└────────────────────────────────────────────┘
To simplify things and lower the amount of devices a user needs to verify,
cross signing has been introduced. Cross signing adds a concept of a user
identity which is represented in the matrix-sdk using the UserIdentity
struct. This way, Alice and Bob only need to verify their own devices and
each other’s user identity for the communication to be considered secure.
┌─────────────────────────────────────────────────┐
│ ┌─────────────────────────────────────────┐ │
▼ │ ▼ │
┌──────────┴─────────┐ ┌───────────────┴──────┐
│┌──────────────────┐│ │ ┌────────────────┐ │
││Alice UserIdentity││ │ │Bob UserIdentity│ │
│└───┬─────────┬────┘│ │ └─┬───────────┬──┘ │
│ │ │ │ │ │ │ │
│ ▼ ▼ │ │ ▼ ▼ │
│┌───────┐ ┌────────┐│ │┌───────┐ ┌─────────┐│
││ Alice │ │ Alice ││ ││ Bob │ │ Bob ││
││ Phone │ │ Laptop ││ ││ Phone │ │ Desktop ││
│└───────┘ └────────┘│ │└───────┘ └─────────┘│
└────────────────────┘ └──────────────────────┘
More info about devices and identities can be found in the identities
module.
To add interactive verification support to your client please see the
verification
module. Also check out the documentation for the
Device::is_verified()
method, which explains in more detail what
it means for a Device
to be verified.
§Client setup
The matrix-sdk aims to provide encryption support transparently. If encryption is enabled and correctly set up, events that need to be encrypted will be encrypted automatically. Events will also be decrypted automatically.
Please note that, unless a client is specifically set up to ignore unverified devices, verifying devices is not necessary for encryption to work.
- Make sure the
e2e-encryption
feature is enabled. - To persist the encryption keys, you can use
ClientBuilder::store_config
or one of the_store
methods onClientBuilder
.
§Restoring a client
Restoring a Client is relatively easy, but there are some things that need to be kept in mind before doing so.
There are two ways one might wish to restore a Client
:
- Using an access token
- Using the password
Initially, logging in creates a device ID and access token on the server. Those two are directly connected to each other — more on this relationship can be found in the spec.
After we log in, the client will upload the end-to-end encryption related device keys to the server. Those device keys cannot be replaced once they have been uploaded and tied to a device ID.
§Using an access token
- Log in with the password using
MatrixAuth::login_username()
. - Store the access token, preferably somewhere secure.
- Use
Client::restore_session()
the next time the client starts.
Note that the access token is directly connected to a device ID that lives on a server. If you’re skipping step one of this method, remember that you can’t use an access token that already has some device keys tied to the device ID.
§Using a password.
- Log in using
MatrixAuth::login_username()
. - Store the
device_id
that was returned in the login response from the server. - Use
MatrixAuth::login_username()
the next time the client starts, make sure to setdevice_id
to the storeddevice_id
from the previous step. This will replace the access token from the previous login call, but won’t create a new device.
Note that the default store supports only a single device. Logging in
with a different device ID (either None
or a device ID of another client)
is not supported using the default store.
§Common pitfalls
Failure | Cause | Fix |
---|---|---|
No messages get encrypted or decrypted | The e2e-encryption feature is disabled | Enable the feature in your Cargo.toml file |
Messages that were decryptable aren’t after a restart | Storage isn’t setup to be persistent | Ensure you’ve activated the persistent storage backend feature, e.g. sqlite |
Messages are encrypted but can’t be decrypted | The access token that the client is using is tied to another device | Clear storage to create a new device, read the Restoring a Client section |
Messages don’t get encrypted but get decrypted | The m.room.encryption event is missing | Make sure encryption is enabled for the room and the event isn’t filtered out. Otherwise it might be a deserialization bug |
Re-exports§
pub use matrix_sdk_base::crypto::vodozemac;
Modules§
- Room key backup support
- Named futures returned from methods on types in the
encryption
module. - Cryptographic identities used in Matrix.
- The recovery module
- Secret Storage Support
- Interactive verification for E2EE capable users and devices in Matrix.
Structs§
- Wraps together a
CrossProcessLockStoreGuard
and a generation number. - A stateful struct remembering the cross-signing keys we need to upload.
- Struct representing the state of our private cross signing keys, it shows which private cross signing keys we have locally stored.
- A high-level API to manage the client’s encryption.
- Settings for end-to-end encryption features.
- Struct holding all the information that is needed to decrypt an encrypted file.
- OIDC specific information about the required authentication for the upload of cross-signing keys.
- Return type for the room key importing.
Enums§
- Settings for end-to-end encryption features.
- information about the additional authentication that is required before the cross-signing keys can be uploaded.
- The crypto store’s error type.
- Error type for attachment decryption.
- Error that occurs when decrypting an event that is malformed.
- Error representing a failure during key export or import.
- The local trust state of a device.
- Error representing a failure during a group encryption operation.
- An error type for the creation of group sessions.
- Error representing a failure during a device to device cryptographic operation.
- An error type for the export of inbound group sessions.
- Error for the room key importing functionality.
- Error describing what went wrong when importing private cross signing keys or the key backup key.
- Error that occurs when a room key can’t be converted into a valid Megolm session.
- Error type describing different errors that happen when we check or create signatures for a Matrix JSON object.
- The verification state of our own device
Constants§
- The version of the matrix-sdk-cypto crate being used