7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1 | // Copyright 2020 The Matrix.org Foundation C.I.C. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 2 | // Copyright 2024 Damir Jelić |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 3 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 5 | // you may not use this file except in compliance with the License. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 6 | // You may obtain a copy of the License at |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 7 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 8 | // http://www.apache.org/licenses/LICENSE-2.0 |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 9 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 10 | // Unless required by applicable law or agreed to in writing, software |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 11 | // distributed under the License is distributed on an "AS IS" BASIS, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 13 | // See the License for the specific language governing permissions and |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 14 | // limitations under the License. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 15 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 16 | #![doc = include_str!("../README.md")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 17 | #![cfg_attr(docsrs, feature(doc_cfg))] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 18 | #![warn(missing_docs, missing_debug_implementations)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 19 | #![cfg_attr(target_family = "wasm", allow(clippy::arc_with_non_send_sync))] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 20 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 21 | pub mod backups; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 22 | mod ciphers; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 23 | pub mod dehydrated_devices; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 24 | mod error; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 25 | mod file_encryption; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 26 | mod gossiping; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 27 | mod identities; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 28 | mod machine; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 29 | pub mod olm; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 30 | pub mod secret_storage; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 31 | mod session_manager; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 32 | pub mod store; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 33 | pub mod types; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 34 | mod utilities; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 35 | mod verification; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 36 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 37 | #[cfg(any(test, feature = "testing"))] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 38 | /// Testing facilities and helpers for crypto tests |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 39 | pub mod testing { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 40 | pub use crate::identities::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 41 | device::testing::get_device, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 42 | user::testing::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 43 | get_other_identity, get_own_identity, simulate_key_query_response_for_verification, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 44 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 45 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 46 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 47 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 48 | use std::collections::{BTreeMap, BTreeSet}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 49 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 50 | pub use identities::room_identity_state::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 51 | IdentityState, IdentityStatusChange, RoomIdentityChange, RoomIdentityProvider, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 52 | RoomIdentityState, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 53 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 54 | use ruma::OwnedRoomId; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 55 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 56 | /// Return type for the room key importing. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 57 | #[derive(Debug, Clone, PartialEq, Eq)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 58 | pub struct RoomKeyImportResult { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 59 | /// The number of room keys that were imported. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 60 | pub imported_count: usize, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 61 | /// The total number of room keys that were found in the export. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 62 | pub total_count: usize, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 63 | /// The map of keys that were imported. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 64 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 65 | /// It's a map from room id to a map of the sender key to a set of session |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 66 | /// ids. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 67 | pub keys: BTreeMap<OwnedRoomId, BTreeMap<String, BTreeSet<String>>>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 68 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 69 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 70 | impl RoomKeyImportResult { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 71 | pub(crate) fn new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 72 | imported_count: usize, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 73 | total_count: usize, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 74 | keys: BTreeMap<OwnedRoomId, BTreeMap<String, BTreeSet<String>>>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 75 | ) -> Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 76 | Self { imported_count, total_count, keys } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 77 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 78 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 79 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 80 | pub use error::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 81 | EventError, MegolmError, OlmError, SessionCreationError, SessionRecipientCollectionError, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 82 | SetRoomSettingsError, SignatureError, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 83 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 84 | pub use file_encryption::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 85 | AttachmentDecryptor, AttachmentEncryptor, DecryptorError, KeyExportError, MediaEncryptionInfo, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 86 | decrypt_room_key_export, encrypt_room_key_export, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 87 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 88 | pub use gossiping::{GossipRequest, GossippedSecret}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 89 | pub use identities::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 90 | Device, DeviceData, LocalTrust, OtherUserIdentity, OtherUserIdentityData, OwnUserIdentity, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 91 | OwnUserIdentityData, UserDevices, UserIdentity, UserIdentityData, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 92 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 93 | pub use machine::{CrossSigningBootstrapRequests, EncryptionSyncChanges, OlmMachine}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 94 | use matrix_sdk_common::deserialized_responses::{DecryptedRoomEvent, UnableToDecryptInfo}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 95 | #[cfg(feature = "qrcode")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 96 | pub use matrix_sdk_qrcode; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 97 | pub use olm::{Account, CrossSigningStatus, EncryptionSettings, Session}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 98 | use serde::{Deserialize, Serialize}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 99 | pub use session_manager::CollectStrategy; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 100 | pub use store::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 101 | CryptoStoreError, SecretImportError, SecretInfo, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 102 | types::{CrossSigningKeyExport, TrackedUser}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 103 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 104 | pub use verification::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 105 | AcceptSettings, AcceptedProtocols, CancelInfo, Emoji, EmojiShortAuthString, Sas, SasState, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 106 | Verification, VerificationRequest, VerificationRequestState, format_emojis, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 107 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 108 | #[cfg(feature = "qrcode")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 109 | pub use verification::{QrVerification, QrVerificationState, ScanError}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 110 | #[doc(no_inline)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 111 | pub use vodozemac; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 112 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 113 | /// The version of the matrix-sdk-cypto crate being used |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 114 | pub const VERSION: &str = env!("CARGO_PKG_VERSION"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 115 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 116 | #[cfg(test)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 117 | matrix_sdk_test_utils::init_tracing_for_tests!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 118 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 119 | #[cfg(feature = "uniffi")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 120 | uniffi::setup_scaffolding!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 121 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 122 | /// The trust level in the sender's device that is required to decrypt an |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 123 | /// event. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 124 | #[derive(Clone, Copy, Debug, Deserialize, Serialize)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 125 | #[cfg_attr(feature = "uniffi", derive(uniffi::Enum))] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 126 | pub enum TrustRequirement { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 127 | /// Decrypt events from everyone regardless of trust. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 128 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 129 | /// Not recommended, per the guidance of [MSC4153]. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 130 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 131 | /// [MSC4153]: https://github.com/matrix-org/matrix-doc/pull/4153 |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 132 | Untrusted, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 133 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 134 | /// Only decrypt events from cross-signed devices or legacy sessions (Megolm |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 135 | /// sessions created before we started collecting trust information). |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 136 | CrossSignedOrLegacy, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 137 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 138 | /// Only decrypt events from cross-signed devices. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 139 | CrossSigned, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 140 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 141 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 142 | /// Settings for decrypting messages |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 143 | #[derive(Clone, Debug, Deserialize, Serialize)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 144 | #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 145 | pub struct DecryptionSettings { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 146 | /// The trust level in the sender's device that is required to decrypt the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 147 | /// event. If the sender's device is not sufficiently trusted, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 148 | /// [`MegolmError::SenderIdentityNotTrusted`] will be returned. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 149 | pub sender_device_trust_requirement: TrustRequirement, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 150 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 151 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 152 | /// The result of an attempt to decrypt a room event: either a successful |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 153 | /// decryption, or information on a failure. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 154 | #[derive(Clone, Debug, Serialize, Deserialize)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 155 | pub enum RoomEventDecryptionResult { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 156 | /// A successfully-decrypted encrypted event. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 157 | Decrypted(DecryptedRoomEvent), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 158 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 159 | /// We were unable to decrypt the event |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 160 | UnableToDecrypt(UnableToDecryptInfo), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 161 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 162 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 163 | #[cfg_attr(doc, aquamarine::aquamarine)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 164 | /// A step by step guide that explains how to include [end-to-end-encryption] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 165 | /// support in a [Matrix] client library. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 166 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 167 | /// This crate implements a [sans-network-io](https://sans-io.readthedocs.io/) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 168 | /// state machine that allows you to add [end-to-end-encryption] support to a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 169 | /// [Matrix] client library. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 170 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 171 | /// This guide aims to provide a comprehensive understanding of end-to-end |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 172 | /// encryption in Matrix without any prior knowledge requirements. However, it |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 173 | /// is recommended that the reader has a basic understanding of Matrix and its |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 174 | /// [client-server specification] for a more informed and efficient learning |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 175 | /// experience. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 176 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 177 | /// The [introductory](#introduction) section provides a simplified explanation |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 178 | /// of end-to-end encryption and its implementation in Matrix for those who may |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 179 | /// not have prior knowledge. If you already have a solid understanding of |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 180 | /// end-to-end encryption, including the [Olm] and [Megolm] protocols, you may |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 181 | /// choose to skip directly to the [Getting Started](#getting-started) section. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 182 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 183 | /// # Table of Contents |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 184 | /// 1. [Introduction](#introduction) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 185 | /// 2. [Getting started](#getting-started) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 186 | /// 3. [Decrypting room events](#decryption) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 187 | /// 4. [Encrypting room events](#encryption) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 188 | /// 5. [Interactively verifying devices and user identities](#verification) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 189 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 190 | /// # Introduction |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 191 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 192 | /// Welcome to the first part of this guide, where we will introduce the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 193 | /// fundamental concepts of end-to-end encryption and its implementation in |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 194 | /// Matrix. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 195 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 196 | /// This section will provide a clear and concise overview of what |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 197 | /// end-to-end encryption is and why it is important for secure communication. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 198 | /// You will also learn about how Matrix uses end-to-end encryption to protect |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 199 | /// the privacy and security of its users' communications. Whether you are new |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 200 | /// to the topic or simply want to improve your understanding, this section will |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 201 | /// serve as a solid foundation for the rest of the guide. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 202 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 203 | /// Let's dive in! |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 204 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 205 | /// ## Notation |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 206 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 207 | /// ## End-to-end-encryption |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 208 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 209 | /// End-to-end encryption (E2EE) is a method of secure communication where only |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 210 | /// the communicating devices, also known as "the ends," can read the data being |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 211 | /// transmitted. This means that the data is encrypted on one device, and can |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 212 | /// only be decrypted on the other device. The server is used only as a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 213 | /// transport mechanism to deliver messages between devices. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 214 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 215 | /// The following chart displays how communication between two clients using a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 216 | /// server in the middle usually works. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 217 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 218 | /// ```mermaid |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 219 | /// flowchart LR |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 220 | /// alice[Alice] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 221 | /// bob[Bob] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 222 | /// subgraph Homeserver |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 223 | /// direction LR |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 224 | /// outbox[Alice outbox] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 225 | /// inbox[Bob inbox] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 226 | /// outbox -. unencrypted .-> inbox |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 227 | /// end |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 228 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 229 | /// alice -- encrypted --> outbox |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 230 | /// inbox -- encrypted --> bob |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 231 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 232 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 233 | /// The next chart, instead, displays how the same flow is happening in a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 234 | /// end-to-end-encrypted world. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 235 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 236 | /// ```mermaid |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 237 | /// flowchart LR |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 238 | /// alice[Alice] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 239 | /// bob[Bob] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 240 | /// subgraph Homeserver |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 241 | /// direction LR |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 242 | /// outbox[Alice outbox] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 243 | /// inbox[Bob inbox] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 244 | /// outbox == encrypted ==> inbox |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 245 | /// end |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 246 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 247 | /// alice == encrypted ==> outbox |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 248 | /// inbox == encrypted ==> bob |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 249 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 250 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 251 | /// Note that the path from the outbox to the inbox is now encrypted as well. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 252 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 253 | /// Alice and Bob have created a secure communication channel |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 254 | /// through which they can exchange messages confidentially, without the risk of |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 255 | /// the server accessing the contents of their messages. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 256 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 257 | /// ## Publishing cryptographic identities of devices |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 258 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 259 | /// If Alice and Bob want to establish a secure channel over which they can |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 260 | /// exchange messages, they first need learn about each others cryptographic |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 261 | /// identities. This is achieved by using the homeserver as a public key |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 262 | /// directory. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 263 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 264 | /// A public key directory is used to store and distribute public keys of users |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 265 | /// in an end-to-end encrypted system. The basic idea behind a public key |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 266 | /// directory is that it allows users to easily discover and download the public |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 267 | /// keys of other users with whom they wish to establish an end-to-end encrypted |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 268 | /// communication. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 269 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 270 | /// Each user generates a pair of public and private keys. The user then uploads |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 271 | /// their public key to the public key directory. Other users can then search |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 272 | /// the directory to find the public key of the user they wish to communicate |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 273 | /// with, and download it to their own device. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 274 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 275 | /// ```mermaid |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 276 | /// flowchart LR |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 277 | /// alice[Alice] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 278 | /// subgraph homeserver[Homeserver] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 279 | /// direction LR |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 280 | /// directory[(Public key directory)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 281 | /// end |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 282 | /// bob[Bob] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 283 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 284 | /// alice -- upload keys --> directory |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 285 | /// directory -- download keys --> bob |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 286 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 287 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 288 | /// Once a user has the other user's public key, they can use it to establish an |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 289 | /// end-to-end encrypted channel using a [key-agreement protocol]. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 290 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 291 | /// ## Using the Triple Diffie-Hellman key-agreement protocol |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 292 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 293 | /// In the triple Diffie-Hellman key agreement protocol (3DH in short), each |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 294 | /// user generates a long-term identity key pair and a set of one-time prekeys. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 295 | /// When two users want to establish a shared secret key, they exchange their |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 296 | /// public identity keys and one of their prekeys. These public keys are then |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 297 | /// used in a [Diffie-Hellman] key exchange to compute a shared secret key. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 298 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 299 | /// The use of one-time prekeys ensures that the shared secret key is different |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 300 | /// for each session, even if the same identity keys are used. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 301 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 302 | /// ```mermaid |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 303 | /// flowchart LR |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 304 | /// subgraph alice_keys[Alice Keys] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 305 | /// direction TB |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 306 | /// alice_key[Alice's identity key] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 307 | /// alice_base_key[Alice's one-time key] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 308 | /// end |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 309 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 310 | /// subgraph bob_keys[Bob Keys] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 311 | /// direction TB |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 312 | /// bob_key[Bob's identity key] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 313 | /// bob_one_time[Bob's one-time key] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 314 | /// end |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 315 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 316 | /// alice_key <--> bob_one_time |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 317 | /// alice_base_key <--> bob_one_time |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 318 | /// alice_base_key <--> bob_key |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 319 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 320 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 321 | /// Similar to [X3DH] (Extended Triple Diffie-Hellman) key agreement protocol |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 322 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 323 | /// ## Speeding up encryption for large groups |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 324 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 325 | /// In the previous section we learned how to utilize a key agreement protocol |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 326 | /// to establish secure 1-to-1 encrypted communication channels. These channels |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 327 | /// allow us to encrypt a message for each device separately. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 328 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 329 | /// One critical property of these channels is that, if you want to send a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 330 | /// message to a group of devices, we'll need to encrypt the message for each |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 331 | /// device individually. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 332 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 333 | /// TODO Explain how megolm fits into this |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 334 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 335 | /// # Getting started |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 336 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 337 | /// Before we start writing any code, let us get familiar with the basic |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 338 | /// principle upon which this library is built. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 339 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 340 | /// The central piece of the library is the [`OlmMachine`] which acts as a state |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 341 | /// machine which consumes data that gets received from the homeserver and |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 342 | /// outputs data which should be sent to the homeserver. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 343 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 344 | /// ## Push/pull mechanism |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 345 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 346 | /// The [`OlmMachine`] at the heart of it acts as a state machine that operates |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 347 | /// in a push/pull manner. HTTP responses which were received from the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 348 | /// homeserver get forwarded into the [`OlmMachine`] and in turn the internal |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 349 | /// state gets updated which produces HTTP requests that need to be sent to the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 350 | /// homeserver. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 351 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 352 | /// In a manner, we're pulling data from the server, we update our internal |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 353 | /// state based on the data and in turn push data back to the server. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 354 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 355 | /// ```mermaid |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 356 | /// flowchart LR |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 357 | /// homeserver[Homeserver] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 358 | /// client[OlmMachine] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 359 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 360 | /// homeserver -- pull --> client |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 361 | /// client -- push --> homeserver |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 362 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 363 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 364 | /// ## Initializing the state machine |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 365 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 366 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 367 | /// use anyhow::Result; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 368 | /// use matrix_sdk_crypto::OlmMachine; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 369 | /// use ruma::user_id; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 370 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 371 | /// # #[tokio::main] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 372 | /// # async fn main() -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 373 | /// let user_id = user_id!("@alice:localhost"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 374 | /// let device_id = "DEVICEID".into(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 375 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 376 | /// let machine = OlmMachine::new(user_id, device_id).await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 377 | /// # Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 378 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 379 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 380 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 381 | /// This will create a [`OlmMachine`] that does not persist any data TODO |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 382 | /// ```ignore |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 383 | /// use anyhow::Result; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 384 | /// use matrix_sdk_crypto::OlmMachine; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 385 | /// use matrix_sdk_sqlite::SqliteCryptoStore; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 386 | /// use ruma::user_id; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 387 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 388 | /// # #[tokio::main] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 389 | /// # async fn main() -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 390 | /// let user_id = user_id!("@alice:localhost"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 391 | /// let device_id = "DEVICEID".into(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 392 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 393 | /// let store = SqliteCryptoStore::open("/home/example/matrix-client/", None).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 394 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 395 | /// let machine = OlmMachine::with_store(user_id, device_id, store).await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 396 | /// # Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 397 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 398 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 399 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 400 | /// # Decryption |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 401 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 402 | /// In the world of encrypted communication, it is common to start with the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 403 | /// encryption step when implementing a protocol. However, in the case of adding |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 404 | /// end-to-end encryption support to a Matrix client library, a simpler approach |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 405 | /// is to first focus on the decryption process. This is because there are |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 406 | /// already Matrix clients in existence that support encryption, which means |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 407 | /// that our client library can simply receive encrypted messages and then |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 408 | /// decrypt them. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 409 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 410 | /// In this section, we will guide you through the minimal steps |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 411 | /// necessary to get the decryption process up and running using the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 412 | /// matrix-sdk-crypto Rust crate. By the end of this section you should have a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 413 | /// Matrix client that is able to decrypt room events that other clients have |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 414 | /// sent. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 415 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 416 | /// To enable decryption the following three steps are needed: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 417 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 418 | /// 1. [The cryptographic identity of your device needs to be published to the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 419 | /// homeserver](#uploading-identity-and-one-time-keys). |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 420 | /// 2. [Decryption keys coming in from other devices need to be processed and |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 421 | /// stored](#receiving-room-keys-and-related-changes). |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 422 | /// 3. [Individual messages need to be decrypted](#decrypting-room-events). |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 423 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 424 | /// The simplified flowchart |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 425 | /// ```mermaid |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 426 | /// graph TD |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 427 | /// sync[Sync with the homeserver] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 428 | /// receive_changes[Push E2EE related changes into the state machine] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 429 | /// send_outgoing_requests[Send all outgoing requests to the homeserver] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 430 | /// decrypt[Process the rest of the sync] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 431 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 432 | /// sync --> receive_changes; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 433 | /// receive_changes --> send_outgoing_requests; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 434 | /// send_outgoing_requests --> decrypt; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 435 | /// decrypt -- repeat --> sync; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 436 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 437 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 438 | /// ## Uploading identity and one-time keys. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 439 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 440 | /// To enable end-to-end encryption in a Matrix client, the first step is to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 441 | /// announce the support for it to other users in the network. This is done by |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 442 | /// publishing the client's long-term device keys and a set of one-time prekeys |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 443 | /// to the Matrix homeserver. The homeserver then makes this information |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 444 | /// available to other devices in the network. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 445 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 446 | /// The long-term device keys and one-time prekeys allow other devices to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 447 | /// encrypt messages specifically for your device. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 448 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 449 | /// To achieve this, you will need to extract any requests that need to be sent |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 450 | /// to the homeserver from the [`OlmMachine`] and send them to the homeserver. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 451 | /// The following snippet showcases how to achieve this using the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 452 | /// [`OlmMachine::outgoing_requests()`] method: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 453 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 454 | /// ```no_run |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 455 | /// # use std::collections::BTreeMap; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 456 | /// # use ruma::api::client::keys::upload_keys::v3::Response; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 457 | /// # use anyhow::Result; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 458 | /// # use matrix_sdk_crypto::{OlmMachine, types::requests::OutgoingRequest}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 459 | /// # async fn send_request(request: OutgoingRequest) -> Result<Response> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 460 | /// # let response = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 461 | /// # Ok(response) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 462 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 463 | /// # #[tokio::main] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 464 | /// # async fn main() -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 465 | /// # let machine: OlmMachine = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 466 | /// // Get all the outgoing requests. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 467 | /// let outgoing_requests = machine.outgoing_requests().await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 468 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 469 | /// // Send each request to the server and push the response into the state machine. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 470 | /// // You can safely send these requests out in parallel. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 471 | /// for request in outgoing_requests { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 472 | /// let request_id = request.request_id(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 473 | /// // Send the request to the server and await a response. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 474 | /// let response = send_request(request).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 475 | /// // Push the response into the state machine. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 476 | /// machine.mark_request_as_sent(&request_id, &response).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 477 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 478 | /// # Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 479 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 480 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 481 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 482 | /// #### 🔒 Locking rule |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 483 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 484 | /// It's important to note that the outgoing requests method in the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 485 | /// [`OlmMachine`], while thread-safe, may return the same request multiple |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 486 | /// times if it is called multiple times before the request has been marked as |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 487 | /// sent. To prevent this issue, it is advisable to encapsulate the outgoing |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 488 | /// request handling logic into a separate helper method and protect it from |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 489 | /// being called multiple times concurrently using a lock. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 490 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 491 | /// This helps to ensure that the request is only handled once and prevents |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 492 | /// multiple identical requests from being sent. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 493 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 494 | /// Additionally, if an error occurs while sending a request using the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 495 | /// [`OlmMachine::outgoing_requests()`] method, the request will be |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 496 | /// naturally retried the next time the method is called. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 497 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 498 | /// A more complete example, which uses a helper method, might look like this: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 499 | /// ```no_run |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 500 | /// # use std::collections::BTreeMap; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 501 | /// # use ruma::api::client::keys::upload_keys::v3::Response; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 502 | /// # use anyhow::Result; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 503 | /// # use matrix_sdk_crypto::{OlmMachine, types::requests::OutgoingRequest}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 504 | /// # async fn send_request(request: &OutgoingRequest) -> Result<Response> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 505 | /// # let response = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 506 | /// # Ok(response) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 507 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 508 | /// # #[tokio::main] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 509 | /// # async fn main() -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 510 | /// struct Client { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 511 | /// outgoing_requests_lock: tokio::sync::Mutex<()>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 512 | /// olm_machine: OlmMachine, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 513 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 514 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 515 | /// async fn process_outgoing_requests(client: &Client) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 516 | /// // Let's acquire a lock so we know that we don't send out the same request out multiple |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 517 | /// // times. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 518 | /// let guard = client.outgoing_requests_lock.lock().await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 519 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 520 | /// for request in client.olm_machine.outgoing_requests().await? { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 521 | /// let request_id = request.request_id(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 522 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 523 | /// match send_request(&request).await { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 524 | /// Ok(response) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 525 | /// client.olm_machine.mark_request_as_sent(&request_id, &response).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 526 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 527 | /// Err(error) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 528 | /// // It's OK to ignore transient HTTP errors since requests will be retried. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 529 | /// eprintln!( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 530 | /// "Error while sending out a end-to-end encryption \ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 531 | /// related request: {error:?}" |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 532 | /// ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 533 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 534 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 535 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 536 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 537 | /// Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 538 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 539 | /// # Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 540 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 541 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 542 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 543 | /// Once we have the helper method that processes our outgoing requests we can |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 544 | /// structure our sync method as follows: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 545 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 546 | /// ```no_run |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 547 | /// # use anyhow::Result; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 548 | /// # use matrix_sdk_crypto::OlmMachine; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 549 | /// # #[tokio::main] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 550 | /// # async fn main() -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 551 | /// # struct Client { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 552 | /// # outgoing_requests_lock: tokio::sync::Mutex<()>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 553 | /// # olm_machine: OlmMachine, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 554 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 555 | /// # async fn process_outgoing_requests(client: &Client) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 556 | /// # unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 557 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 558 | /// # async fn send_out_sync_request(client: &Client) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 559 | /// # unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 560 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 561 | /// async fn sync(client: &Client) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 562 | /// // This is happening at the top of the method so we advertise our |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 563 | /// // end-to-end encryption capabilities as soon as possible. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 564 | /// process_outgoing_requests(client).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 565 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 566 | /// // We can sync with the homeserver now. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 567 | /// let response = send_out_sync_request(client).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 568 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 569 | /// // Process the sync response here. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 570 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 571 | /// Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 572 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 573 | /// # Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 574 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 575 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 576 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 577 | /// ## Receiving room keys and related changes |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 578 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 579 | /// The next step in our implementation is to forward messages that were sent |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 580 | /// directly to the client's device, and state updates about the one-time |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 581 | /// prekeys, to the [`OlmMachine`]. This is achieved using |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 582 | /// the [`OlmMachine::receive_sync_changes()`] method. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 583 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 584 | /// The method performs two tasks: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 585 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 586 | /// 1. It processes and, if necessary, decrypts each [to-device] event that was |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 587 | /// pushed into it, and returns the decrypted events. The original events are |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 588 | /// replaced with their decrypted versions. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 589 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 590 | /// 2. It produces internal state changes that may trigger the creation of new |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 591 | /// outgoing requests. For example, if the server informs the client that its |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 592 | /// one-time prekeys have been depleted, the OlmMachine will create an |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 593 | /// outgoing request to replenish them. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 594 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 595 | /// Our updated sync method now looks like this: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 596 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 597 | /// ```no_run |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 598 | /// # use anyhow::Result; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 599 | /// # use matrix_sdk_crypto::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 600 | /// DecryptionSettings, EncryptionSyncChanges, OlmMachine, TrustRequirement |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 601 | /// }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 602 | /// # use ruma::api::client::sync::sync_events::v3::Response; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 603 | /// # #[tokio::main] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 604 | /// # async fn main() -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 605 | /// # struct Client { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 606 | /// # outgoing_requests_lock: tokio::sync::Mutex<()>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 607 | /// # olm_machine: OlmMachine, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 608 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 609 | /// # async fn process_outgoing_requests(client: &Client) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 610 | /// # unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 611 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 612 | /// # async fn send_out_sync_request(client: &Client) -> Result<Response> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 613 | /// # unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 614 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 615 | /// async fn sync(client: &Client) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 616 | /// process_outgoing_requests(client).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 617 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 618 | /// let response = send_out_sync_request(client).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 619 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 620 | /// let sync_changes = EncryptionSyncChanges { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 621 | /// to_device_events: response.to_device.events, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 622 | /// changed_devices: &response.device_lists, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 623 | /// one_time_keys_counts: &response.device_one_time_keys_count, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 624 | /// unused_fallback_keys: response.device_unused_fallback_key_types.as_deref(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 625 | /// next_batch_token: Some(response.next_batch), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 626 | /// }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 627 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 628 | /// let decryption_settings = DecryptionSettings { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 629 | /// sender_device_trust_requirement: TrustRequirement::Untrusted |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 630 | /// }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 631 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 632 | /// // Push the sync changes into the OlmMachine, make sure that this is |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 633 | /// // happening before the `next_batch` token of the sync is persisted. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 634 | /// let to_device_events = client |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 635 | /// .olm_machine |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 636 | /// .receive_sync_changes(sync_changes, &decryption_settings) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 637 | /// .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 638 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 639 | /// // Send the outgoing requests out that the sync changes produced. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 640 | /// process_outgoing_requests(client).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 641 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 642 | /// // Process the rest of the sync response here. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 643 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 644 | /// Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 645 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 646 | /// # Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 647 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 648 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 649 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 650 | /// It is important to note that the names of the fields in the response shown |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 651 | /// in the example match the names of the fields specified in the [sync] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 652 | /// response specification. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 653 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 654 | /// It is critical to note that due to the ephemeral nature of to-device |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 655 | /// events[[1]], it is important to process these events before persisting the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 656 | /// `next_batch` sync token. This is because if the `next_batch` sync token is |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 657 | /// persisted before processing the to-device events, some messages might be |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 658 | /// lost, leading to decryption failures. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 659 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 660 | /// ## Decrypting room events |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 661 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 662 | /// The final step in the decryption process is to decrypt the room events that |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 663 | /// are received from the server. To do this, the encrypted events must be |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 664 | /// passed to the [`OlmMachine`], which will use the keys that were previously |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 665 | /// exchanged between devices to decrypt the events. The decrypted events can |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 666 | /// then be processed and displayed to the user in the Matrix client. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 667 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 668 | /// Room message [events] can be decrypted using the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 669 | /// [`OlmMachine::decrypt_room_event()`] method: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 670 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 671 | /// ```no_run |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 672 | /// # use std::collections::BTreeMap; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 673 | /// # use anyhow::Result; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 674 | /// # use matrix_sdk_crypto::{OlmMachine, DecryptionSettings, TrustRequirement}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 675 | /// # #[tokio::main] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 676 | /// # async fn main() -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 677 | /// # let encrypted = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 678 | /// # let room_id = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 679 | /// # let machine: OlmMachine = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 680 | /// # let settings = DecryptionSettings { sender_device_trust_requirement: TrustRequirement::Untrusted }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 681 | /// // Decrypt your room events now. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 682 | /// let decrypted = machine |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 683 | /// .decrypt_room_event(encrypted, room_id, &settings) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 684 | /// .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 685 | /// # Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 686 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 687 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 688 | /// It's worth mentioning that the [`OlmMachine::decrypt_room_event()`] method |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 689 | /// is designed to be thread-safe and can be safely called concurrently. This |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 690 | /// means that room message [events] can be processed in parallel, improving the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 691 | /// overall efficiency of the end-to-end encryption implementation. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 692 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 693 | /// By allowing room message [events] to be processed concurrently, the client's |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 694 | /// implementation can take full advantage of the capabilities of modern |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 695 | /// hardware and achieve better performance, especially when dealing with a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 696 | /// large number of messages at once. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 697 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 698 | /// # Encryption |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 699 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 700 | /// In this section of the guide, we will focus on enabling the encryption of |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 701 | /// messages in our Matrix client library. Up until this point, we have been |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 702 | /// discussing the process of decrypting messages that have been encrypted by |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 703 | /// other devices. Now, we will shift our focus to the process of encrypting |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 704 | /// messages on the client side, so that they can be securely transmitted over |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 705 | /// the Matrix network to other devices. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 706 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 707 | /// This section will guide you through the steps required to set up the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 708 | /// encryption process, including establishing the necessary sessions and |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 709 | /// encrypting messages using the Megolm group session. The specific steps are |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 710 | /// outlined below: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 711 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 712 | /// 1. [Cryptographic devices of other users need to be |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 713 | /// discovered](#tracking-users) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 714 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 715 | /// 2. [Secure channels between the devices need to be |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 716 | /// established](#establishing-end-to-end-encrypted-channels) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 717 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 718 | /// 3. [A room key needs to be exchanged with the group](#exchanging-room-keys) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 719 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 720 | /// 4. [Individual messages need to be encrypted using the room |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 721 | /// key](#encrypting-room-events) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 722 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 723 | /// The process for enabling encryption in a two-device scenario is also |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 724 | /// depicted in the following sequence diagram: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 725 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 726 | /// ```mermaid |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 727 | /// sequenceDiagram |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 728 | /// actor Alice |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 729 | /// participant Homeserver |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 730 | /// actor Bob |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 731 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 732 | /// Alice->>Homeserver: Download Bob's one-time prekey |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 733 | /// Homeserver->>Alice: Bob's one-time prekey |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 734 | /// Alice->>Alice: Encrypt the room key |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 735 | /// Alice->>Homeserver: Send the room key to each of Bob's devices |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 736 | /// Homeserver->>Bob: Deliver the room key |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 737 | /// Alice->>Alice: Encrypt the message |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 738 | /// Alice->>Homeserver: Send the encrypted message |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 739 | /// Homeserver->>Bob: Deliver the encrypted message |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 740 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 741 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 742 | /// In the following subsections, we will provide a step-by-step guide on how to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 743 | /// enable the encryption of messages using the OlmMachine. We will outline the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 744 | /// specific method calls and usage patterns that are required to establish the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 745 | /// necessary sessions, encrypt messages, and send them over the Matrix network. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 746 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 747 | /// ## Tracking users |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 748 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 749 | /// The first step in the process of encrypting a message and sending it to a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 750 | /// device is to discover the devices that the recipient user has. This can be |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 751 | /// achieved by sending a request to the homeserver to retrieve a list of the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 752 | /// recipient's device keys. The response to this request will include the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 753 | /// device keys for all of the devices that belong to the recipient, as well as |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 754 | /// information about their current status and whether or not they support |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 755 | /// end-to-end encryption. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 756 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 757 | /// The process for discovering and keeping track of devices for a user is |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 758 | /// outlined in the Matrix specification in the "[Tracking the device list for a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 759 | /// user]" section. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 760 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 761 | /// A simplified sequence diagram of the process can also be found below. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 762 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 763 | /// ```mermaid |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 764 | /// sequenceDiagram |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 765 | /// actor Alice |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 766 | /// participant Homeserver |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 767 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 768 | /// Alice->>Homeserver: Sync with the homeserver |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 769 | /// Homeserver->>Alice: Users whose device list has changed |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 770 | /// Alice->>Alice: Mark user's devicel list as outdated |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 771 | /// Alice->>Homeserver: Ask the server for the new device list of all the outdated users |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 772 | /// Alice->>Alice: Update the local device list and mark the users as up-to-date |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 773 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 774 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 775 | /// The OlmMachine refers to users whose devices we are tracking as "tracked |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 776 | /// users" and utilizes the [`OlmMachine::update_tracked_users()`] method to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 777 | /// start considering users to be tracked. Keeping the above diagram in mind, we |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 778 | /// can now update our sync method as follows: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 779 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 780 | /// ```no_run |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 781 | /// # use anyhow::Result; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 782 | /// # use std::ops::Deref; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 783 | /// # use matrix_sdk_crypto::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 784 | /// # DecryptionSettings, EncryptionSyncChanges, OlmMachine, TrustRequirement |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 785 | /// # }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 786 | /// # use ruma::api::client::sync::sync_events::v3::{Response, State, JoinedRoom}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 787 | /// # use ruma::{OwnedUserId, serde::Raw, events::AnySyncStateEvent}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 788 | /// # #[tokio::main] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 789 | /// # async fn main() -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 790 | /// # struct Client { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 791 | /// # outgoing_requests_lock: tokio::sync::Mutex<()>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 792 | /// # olm_machine: OlmMachine, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 793 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 794 | /// # async fn process_outgoing_requests(client: &Client) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 795 | /// # unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 796 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 797 | /// # async fn send_out_sync_request(client: &Client) -> Result<Response> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 798 | /// # unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 799 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 800 | /// # fn is_member_event_of_a_joined_user(event: &Raw<AnySyncStateEvent>) -> bool { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 801 | /// # true |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 802 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 803 | /// # fn get_user_id(event: &Raw<AnySyncStateEvent>) -> OwnedUserId { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 804 | /// # unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 805 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 806 | /// # fn is_room_encrypted(room: &JoinedRoom) -> bool { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 807 | /// # true |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 808 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 809 | /// async fn sync(client: &Client) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 810 | /// process_outgoing_requests(client).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 811 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 812 | /// let response = send_out_sync_request(client).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 813 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 814 | /// let sync_changes = EncryptionSyncChanges { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 815 | /// to_device_events: response.to_device.events, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 816 | /// changed_devices: &response.device_lists, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 817 | /// one_time_keys_counts: &response.device_one_time_keys_count, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 818 | /// unused_fallback_keys: response.device_unused_fallback_key_types.as_deref(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 819 | /// next_batch_token: Some(response.next_batch), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 820 | /// }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 821 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 822 | /// let decryption_settings = DecryptionSettings { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 823 | /// sender_device_trust_requirement: TrustRequirement::Untrusted |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 824 | /// }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 825 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 826 | /// // Push the sync changes into the OlmMachine, make sure that this is |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 827 | /// // happening before the `next_batch` token of the sync is persisted. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 828 | /// let to_device_events = client |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 829 | /// .olm_machine |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 830 | /// .receive_sync_changes(sync_changes, &decryption_settings) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 831 | /// .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 832 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 833 | /// // Send the outgoing requests out that the sync changes produced. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 834 | /// process_outgoing_requests(client).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 835 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 836 | /// // Collect all the joined and invited users of our end-to-end encrypted rooms here. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 837 | /// let mut users = Vec::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 838 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 839 | /// for (_, room) in &response.rooms.join { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 840 | /// // For simplicity reasons we're only looking at the state field of a joined room, but |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 841 | /// // the events in the timeline are important as well. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 842 | /// if let State::Before(state) = &room.state { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 843 | /// for event in &state.events { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 844 | /// if is_member_event_of_a_joined_user(event) && is_room_encrypted(room) { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 845 | /// let user_id = get_user_id(event); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 846 | /// users.push(user_id); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 847 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 848 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 849 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 850 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 851 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 852 | /// // Mark all the users that we consider to be in a end-to-end encrypted room with us to be |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 853 | /// // tracked. We need to know about all the devices each user has so we can later encrypt |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 854 | /// // messages for each of their devices. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 855 | /// client.olm_machine.update_tracked_users(users.iter().map(Deref::deref)).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 856 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 857 | /// // Process the rest of the sync response here. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 858 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 859 | /// Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 860 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 861 | /// # Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 862 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 863 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 864 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 865 | /// Now that we have discovered the devices of the users we'd like to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 866 | /// communicate with in an end-to-end encrypted manner, we can start considering |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 867 | /// encrypting messages for those devices. This concludes the sync processing |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 868 | /// method, we are now ready to move on to the next section, which will explain |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 869 | /// how to begin the encryption process. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 870 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 871 | /// ## Establishing end-to-end encrypted channels |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 872 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 873 | /// In the [Triple |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 874 | /// Diffie-Hellman](#using-the-triple-diffie-hellman-key-agreement-protocol) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 875 | /// section, we described the need for two Curve25519 keys from the recipient |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 876 | /// device to establish a 1-to-1 secure channel: the long-term identity key of a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 877 | /// device and a one-time prekey. In the previous section, we started tracking |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 878 | /// the device keys, including the long-term identity key that we need. The next |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 879 | /// step is to download the one-time prekey on an on-demand basis and establish |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 880 | /// the 1-to-1 secure channel. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 881 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 882 | /// To accomplish this, we can use the [`OlmMachine::get_missing_sessions()`] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 883 | /// method in bulk, which will claim the one-time prekey for all the devices of |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 884 | /// a user that we're not already sharing a 1-to-1 encrypted channel with. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 885 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 886 | /// #### 🔒 Locking rule |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 887 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 888 | /// As with the [`OlmMachine::outgoing_requests()`] method, it is necessary to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 889 | /// protect this method with a lock, otherwise we will be creating more 1-to-1 |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 890 | /// encrypted channels than necessary. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 891 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 892 | /// ```no_run |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 893 | /// # use std::collections::{BTreeMap, HashSet}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 894 | /// # use std::ops::Deref; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 895 | /// # use anyhow::Result; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 896 | /// # use ruma::UserId; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 897 | /// # use ruma::api::client::keys::claim_keys::v3::{Response, Request}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 898 | /// # use matrix_sdk_crypto::OlmMachine; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 899 | /// # async fn send_request(request: &Request) -> Result<Response> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 900 | /// # let response = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 901 | /// # Ok(response) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 902 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 903 | /// # #[tokio::main] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 904 | /// # async fn main() -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 905 | /// # let users: HashSet<&UserId> = HashSet::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 906 | /// # let machine: OlmMachine = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 907 | /// // Mark all the users that are part of an encrypted room as tracked |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 908 | /// if let Some((request_id, request)) = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 909 | /// machine.get_missing_sessions(users.iter().map(Deref::deref)).await? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 910 | /// { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 911 | /// let response = send_request(&request).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 912 | /// machine.mark_request_as_sent(&request_id, &response).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 913 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 914 | /// # Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 915 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 916 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 917 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 918 | /// With the ability to exchange messages directly with devices, we can now |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 919 | /// start sharing room keys over the 1-to-1 encrypted channel. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 920 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 921 | /// ## Exchanging room keys |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 922 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 923 | /// To exchange a room key with our group, we will once again take a bulk |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 924 | /// approach. The [`OlmMachine::share_room_key()`] method is used to accomplish |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 925 | /// this step. This method will create a new room key, if necessary, and encrypt |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 926 | /// it for each device belonging to the users provided as an argument. It will |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 927 | /// then output an array of sendToDevice requests that we must send to the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 928 | /// server, and mark the requests as sent. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 929 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 930 | /// #### 🔒 Locking rule |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 931 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 932 | /// Like some of the previous methods, OlmMachine::share_room_key() needs to be |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 933 | /// protected by a lock to prevent the possibility of creating and sending |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 934 | /// multiple room keys simultaneously for the same group. The lock can be |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 935 | /// implemented on a per-room basis, which allows for parallel room key |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 936 | /// exchanges across different rooms. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 937 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 938 | /// ```no_run |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 939 | /// # use std::collections::{BTreeMap, HashSet}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 940 | /// # use std::ops::Deref; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 941 | /// # use anyhow::Result; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 942 | /// # use ruma::UserId; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 943 | /// # use ruma::api::client::keys::claim_keys::v3::{Response, Request}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 944 | /// # use matrix_sdk_crypto::{OlmMachine, types::requests::ToDeviceRequest, EncryptionSettings}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 945 | /// # async fn send_request(request: &ToDeviceRequest) -> Result<Response> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 946 | /// # let response = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 947 | /// # Ok(response) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 948 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 949 | /// # #[tokio::main] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 950 | /// # async fn main() -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 951 | /// # let users: HashSet<&UserId> = HashSet::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 952 | /// # let room_id = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 953 | /// # let settings = EncryptionSettings::default(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 954 | /// # let machine: OlmMachine = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 955 | /// // Let's share a room key with our group. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 956 | /// let requests = machine.share_room_key( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 957 | /// room_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 958 | /// users.iter().map(Deref::deref), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 959 | /// EncryptionSettings::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 960 | /// ).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 961 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 962 | /// // Make sure each request is sent out |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 963 | /// for request in requests { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 964 | /// let request_id = &request.txn_id; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 965 | /// let response = send_request(&request).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 966 | /// machine.mark_request_as_sent(&request_id, &response).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 967 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 968 | /// # Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 969 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 970 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 971 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 972 | /// In order to ensure that room keys are rotated and exchanged when needed, the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 973 | /// [`OlmMachine::share_room_key()`] method should be called before sending |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 974 | /// each room message in an end-to-end encrypted room. If a room key has |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 975 | /// already been exchanged, the method becomes a no-op. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 976 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 977 | /// ## Encrypting room events |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 978 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 979 | /// After the room key has been successfully shared, a plaintext can be |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 980 | /// encrypted. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 981 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 982 | /// ```no_run |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 983 | /// # use anyhow::Result; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 984 | /// # use matrix_sdk_crypto::{DecryptionSettings, OlmMachine, TrustRequirement}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 985 | /// # use ruma::events::{AnyMessageLikeEventContent, room::message::RoomMessageEventContent}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 986 | /// # #[tokio::main] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 987 | /// # async fn main() -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 988 | /// # let room_id = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 989 | /// # let event = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 990 | /// # let machine: OlmMachine = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 991 | /// # let settings = DecryptionSettings { sender_device_trust_requirement: TrustRequirement::Untrusted }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 992 | /// let content = AnyMessageLikeEventContent::RoomMessage(RoomMessageEventContent::text_plain("It's a secret to everybody.")); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 993 | /// let encrypted_content = machine.encrypt_room_event(room_id, content).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 994 | /// # Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 995 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 996 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 997 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 998 | /// ## Appendix: Combining the session creation and room key exchange |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 999 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1000 | /// The steps from the previous three sections should combined into a single |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1001 | /// method that is used to send messages. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1002 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1003 | /// ```no_run |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1004 | /// # use std::collections::{BTreeMap, HashSet}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1005 | /// # use std::ops::Deref; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1006 | /// # use anyhow::Result; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1007 | /// # use serde_json::json; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1008 | /// # use ruma::{UserId, RoomId, serde::Raw}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1009 | /// # use ruma::api::client::keys::claim_keys::v3::{Response, Request}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1010 | /// # use matrix_sdk_crypto::{EncryptionSettings, OlmMachine, types::requests::ToDeviceRequest}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1011 | /// # use tokio::sync::MutexGuard; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1012 | /// # async fn send_request(request: &Request) -> Result<Response> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1013 | /// # let response = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1014 | /// # Ok(response) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1015 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1016 | /// # async fn send_to_device_request(request: &ToDeviceRequest) -> Result<Response> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1017 | /// # let response = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1018 | /// # Ok(response) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1019 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1020 | /// # async fn acquire_per_room_lock(room_id: &RoomId) -> MutexGuard<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1021 | /// # unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1022 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1023 | /// # async fn get_joined_members(room_id: &RoomId) -> Vec<&UserId> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1024 | /// # unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1025 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1026 | /// # fn is_room_encrypted(room_id: &RoomId) -> bool { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1027 | /// # true |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1028 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1029 | /// # #[tokio::main] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1030 | /// # async fn main() -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1031 | /// # let users: HashSet<&UserId> = HashSet::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1032 | /// # let machine: OlmMachine = unimplemented!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1033 | /// struct Client { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1034 | /// session_establishment_lock: tokio::sync::Mutex<()>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1035 | /// olm_machine: OlmMachine, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1036 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1037 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1038 | /// async fn establish_sessions(client: &Client, users: &[&UserId]) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1039 | /// if let Some((request_id, request)) = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1040 | /// client.olm_machine.get_missing_sessions(users.iter().map(Deref::deref)).await? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1041 | /// { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1042 | /// let response = send_request(&request).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1043 | /// client.olm_machine.mark_request_as_sent(&request_id, &response).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1044 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1045 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1046 | /// Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1047 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1048 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1049 | /// async fn share_room_key(machine: &OlmMachine, room_id: &RoomId, users: &[&UserId]) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1050 | /// let _lock = acquire_per_room_lock(room_id).await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1051 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1052 | /// let requests = machine.share_room_key( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1053 | /// room_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1054 | /// users.iter().map(Deref::deref), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1055 | /// EncryptionSettings::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1056 | /// ).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1057 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1058 | /// // Make sure each request is sent out |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1059 | /// for request in requests { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1060 | /// let request_id = &request.txn_id; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1061 | /// let response = send_to_device_request(&request).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1062 | /// machine.mark_request_as_sent(&request_id, &response).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1063 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1064 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1065 | /// Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1066 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1067 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1068 | /// async fn send_message(client: &Client, room_id: &RoomId, message: &str) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1069 | /// let mut content = json!({ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1070 | /// "body": message, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1071 | /// "msgtype": "m.text", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1072 | /// }); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1073 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1074 | /// if is_room_encrypted(room_id) { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1075 | /// let content = Raw::new(&json!({ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1076 | /// "body": message, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1077 | /// "msgtype": "m.text", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1078 | /// }))?.cast_unchecked(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1079 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1080 | /// let users = get_joined_members(room_id).await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1081 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1082 | /// establish_sessions(client, &users).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1083 | /// share_room_key(&client.olm_machine, room_id, &users).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1084 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1085 | /// let encrypted = client |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1086 | /// .olm_machine |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1087 | /// .encrypt_room_event_raw(room_id, "m.room.message", &content) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1088 | /// .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1089 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1090 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1091 | /// Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1092 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1093 | /// # Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1094 | /// # } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1095 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1096 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1097 | /// TODO |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1098 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1099 | /// [Matrix]: https://matrix.org/ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1100 | /// [Olm]: https://gitlab.matrix.org/matrix-org/olm/-/blob/master/docs/olm.md |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1101 | /// [Diffie-Hellman]: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1102 | /// [Megolm]: https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/megolm.md |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1103 | /// [end-to-end-encryption]: https://en.wikipedia.org/wiki/End-to-end_encryption |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1104 | /// [homeserver]: https://spec.matrix.org/unstable/#architecture |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1105 | /// [key-agreement protocol]: https://en.wikipedia.org/wiki/Key-agreement_protocol |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1106 | /// [client-server specification]: https://spec.matrix.org/latest/client-server-api |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1107 | /// [forward secrecy]: https://en.wikipedia.org/wiki/Forward_secrecy |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1108 | /// [replay attacks]: https://en.wikipedia.org/wiki/Replay_attack |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1109 | /// [Tracking the device list for a user]: https://spec.matrix.org/unstable/client-server-api/#tracking-the-device-list-for-a-user |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1110 | /// [X3DH]: https://signal.org/docs/specifications/x3dh/ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1111 | /// [to-device]: https://spec.matrix.org/unstable/client-server-api/#send-to-device-messaging |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1112 | /// [sync]: https://spec.matrix.org/unstable/client-server-api/#get_matrixclientv3sync |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1113 | /// [events]: https://spec.matrix.org/unstable/client-server-api/#events |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1114 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1115 | /// [1]: https://spec.matrix.org/unstable/client-server-api/#server-behaviour-4 |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1116 | pub mod tutorial {} |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1117 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1118 | #[cfg(test)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1119 | mod test { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1120 | use insta::assert_json_snapshot; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1121 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1122 | use crate::{DecryptionSettings, TrustRequirement}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1123 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1124 | #[test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1125 | fn snapshot_trust_requirement() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1126 | assert_json_snapshot!(TrustRequirement::Untrusted); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1127 | assert_json_snapshot!(TrustRequirement::CrossSignedOrLegacy); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1128 | assert_json_snapshot!(TrustRequirement::CrossSigned); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1129 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1130 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1131 | #[test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1132 | fn snapshot_decryption_settings() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1133 | assert_json_snapshot!(DecryptionSettings { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1134 | sender_device_trust_requirement: TrustRequirement::Untrusted, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1135 | }); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1136 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1137 | } |