7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1 | use std::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 2 | collections::{BTreeMap, HashMap}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 3 | io::Cursor, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 4 | mem::ManuallyDrop, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 5 | ops::Deref, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 6 | sync::Arc, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 7 | time::Duration, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 8 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 9 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 10 | use js_int::UInt; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 11 | use matrix_sdk_common::deserialized_responses::AlgorithmInfo; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 12 | use matrix_sdk_crypto::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 13 | CollectStrategy, DecryptionSettings, LocalTrust, OlmMachine as InnerMachine, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 14 | UserIdentity as SdkUserIdentity, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 15 | backups::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 16 | MegolmV1BackupKey as RustBackupKey, SignatureState, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 17 | SignatureVerification as RustSignatureCheckResult, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 18 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 19 | decrypt_room_key_export, encrypt_room_key_export, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 20 | olm::ExportedRoomKey, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 21 | store::types::{BackupDecryptionKey, Changes}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 22 | types::requests::ToDeviceRequest, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 23 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 24 | use ruma::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 25 | DeviceKeyAlgorithm, EventId, OneTimeKeyAlgorithm, OwnedTransactionId, OwnedUserId, RoomId, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 26 | UserId, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 27 | api::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 28 | IncomingResponse, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 29 | client::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 30 | backup::add_backup_keys::v3::Response as KeysBackupResponse, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 31 | keys::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 32 | claim_keys::v3::Response as KeysClaimResponse, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 33 | get_keys::v3::Response as KeysQueryResponse, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 34 | upload_keys::v3::Response as KeysUploadResponse, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 35 | upload_signatures::v3::Response as SignatureUploadResponse, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 36 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 37 | message::send_message_event::v3::Response as RoomMessageResponse, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 38 | sync::sync_events::{DeviceLists as RumaDeviceLists, v3::ToDevice}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 39 | to_device::send_event_to_device::v3::Response as ToDeviceResponse, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 40 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 41 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 42 | events::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 43 | AnyMessageLikeEvent, AnySyncMessageLikeEvent, AnyTimelineEvent, MessageLikeEvent, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 44 | key::verification::VerificationMethod, room::message::MessageType, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 45 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 46 | serde::Raw, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 47 | to_device::DeviceIdOrAllDevices, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 48 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 49 | use serde::{Deserialize, Serialize}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 50 | use serde_json::{Value, value::RawValue}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 51 | use tokio::runtime::Runtime; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 52 | use zeroize::Zeroize; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 53 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 54 | use crate::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 55 | BackupKeys, BackupRecoveryKey, BootstrapCrossSigningResult, CrossSigningKeyExport, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 56 | CrossSigningStatus, DecodeError, DecryptedEvent, Device, DeviceLists, EncryptionSettings, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 57 | EventEncryptionAlgorithm, KeyImportError, KeysImportResult, MegolmV1BackupKey, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 58 | ProgressListener, Request, RequestType, RequestVerificationResult, RoomKeyCounts, RoomSettings, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 59 | Sas, SignatureUploadRequest, StartSasResult, UserIdentity, Verification, VerificationRequest, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 60 | dehydrated_devices::DehydratedDevices, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 61 | error::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 62 | CryptoStoreError, DecryptionError, SecretImportError, SecretsBundleExportError, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 63 | SignatureError, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 64 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 65 | parse_user_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 66 | responses::{OwnedResponse, response_from_string}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 67 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 68 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 69 | /// The return value for the [`OlmMachine::receive_sync_changes()`] method. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 70 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 71 | /// Will contain various information about the `/sync` changes the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 72 | /// [`OlmMachine`] processed. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 73 | #[derive(uniffi::Record)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 74 | pub struct SyncChangesResult { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 75 | /// The, now possibly decrypted, to-device events the [`OlmMachine`] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 76 | /// received, decrypted, and processed. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 77 | to_device_events: Vec<String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 78 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 79 | /// Information about the room keys that were extracted out of the to-device |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 80 | /// events. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 81 | room_key_infos: Vec<RoomKeyInfo>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 82 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 83 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 84 | /// Information on a room key that has been received or imported. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 85 | #[derive(uniffi::Record)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 86 | pub struct RoomKeyInfo { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 87 | /// The [messaging algorithm] that this key is used for. Will be one of the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 88 | /// `m.megolm.*` algorithms. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 89 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 90 | /// [messaging algorithm]: https://spec.matrix.org/v1.6/client-server-api/#messaging-algorithms |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 91 | pub algorithm: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 92 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 93 | /// The room where the key is used. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 94 | pub room_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 95 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 96 | /// The Curve25519 key of the device which initiated the session originally. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 97 | pub sender_key: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 98 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 99 | /// The ID of the session that the key is for. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 100 | pub session_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 101 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 102 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 103 | impl From<matrix_sdk_crypto::store::types::RoomKeyInfo> for RoomKeyInfo { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 104 | fn from(value: matrix_sdk_crypto::store::types::RoomKeyInfo) -> Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 105 | Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 106 | algorithm: value.algorithm.to_string(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 107 | room_id: value.room_id.to_string(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 108 | sender_key: value.sender_key.to_base64(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 109 | session_id: value.session_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 110 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 111 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 112 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 113 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 114 | /// A high level state machine that handles E2EE for Matrix. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 115 | #[derive(uniffi::Object)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 116 | pub struct OlmMachine { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 117 | pub(crate) inner: ManuallyDrop<InnerMachine>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 118 | pub(crate) runtime: Runtime, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 119 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 120 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 121 | impl Drop for OlmMachine { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 122 | fn drop(&mut self) { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 123 | // Dropping the inner OlmMachine must happen within a tokio context |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 124 | // because deadpool drops sqlite connections in the DB pool on tokio's |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 125 | // blocking threadpool to avoid blocking async worker threads. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 126 | let _guard = self.runtime.enter(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 127 | // SAFETY: self.inner is never used again, which is the only requirement |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 128 | // for ManuallyDrop::drop to be used safely. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 129 | unsafe { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 130 | ManuallyDrop::drop(&mut self.inner); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 131 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 132 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 133 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 134 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 135 | /// A pair of outgoing room key requests, both of those are sendToDevice |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 136 | /// requests. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 137 | #[derive(uniffi::Record)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 138 | pub struct KeyRequestPair { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 139 | /// The optional cancellation, this is None if no previous key request was |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 140 | /// sent out for this key, thus it doesn't need to be cancelled. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 141 | pub cancellation: Option<Request>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 142 | /// The actual key request. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 143 | pub key_request: Request, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 144 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 145 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 146 | /// The result of a signature verification of a signed JSON object. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 147 | #[derive(Clone, Debug, PartialEq, Eq, uniffi::Record)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 148 | pub struct SignatureVerification { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 149 | /// The result of the signature verification using the public key of our own |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 150 | /// device. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 151 | pub device_signature: SignatureState, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 152 | /// The result of the signature verification using the public key of our own |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 153 | /// user identity. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 154 | pub user_identity_signature: SignatureState, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 155 | /// The result of the signature verification using public keys of other |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 156 | /// devices we own. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 157 | pub other_devices_signatures: HashMap<String, SignatureState>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 158 | /// Is the signed JSON object trusted. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 159 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 160 | /// This flag tells us if the result has a valid signature from any of the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 161 | /// following: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 162 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 163 | /// * Our own device |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 164 | /// * Our own user identity, provided the identity is trusted as well |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 165 | /// * Any of our own devices, provided the device is trusted as well |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 166 | pub trusted: bool, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 167 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 168 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 169 | impl From<RustSignatureCheckResult> for SignatureVerification { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 170 | fn from(r: RustSignatureCheckResult) -> Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 171 | let trusted = r.trusted(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 172 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 173 | Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 174 | device_signature: r.device_signature, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 175 | user_identity_signature: r.user_identity_signature, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 176 | other_devices_signatures: r |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 177 | .other_signatures |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 178 | .into_iter() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 179 | .map(|(k, v)| (k.to_string(), v)) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 180 | .collect(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 181 | trusted, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 182 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 183 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 184 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 185 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 186 | #[matrix_sdk_ffi_macros::export] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 187 | impl OlmMachine { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 188 | /// Create a new `OlmMachine` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 189 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 190 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 191 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 192 | /// * `user_id` - The unique ID of the user that owns this machine. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 193 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 194 | /// * `device_id` - The unique ID of the device that owns this machine. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 195 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 196 | /// * `path` - The path where the state of the machine should be persisted. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 197 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 198 | /// * `passphrase` - The passphrase that should be used to encrypt the data |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 199 | /// at rest in the crypto store. **Warning**, if no passphrase is given, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 200 | /// the store and all its data will remain unencrypted. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 201 | #[uniffi::constructor] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 202 | pub fn new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 203 | user_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 204 | device_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 205 | path: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 206 | mut passphrase: Option<String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 207 | ) -> Result<Arc<Self>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 208 | let user_id = parse_user_id(&user_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 209 | let device_id = device_id.as_str().into(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 210 | let runtime = Runtime::new().expect("Couldn't create a tokio runtime"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 211 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 212 | let store = runtime |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 213 | .block_on(matrix_sdk_sqlite::SqliteCryptoStore::open(path, passphrase.as_deref()))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 214 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 215 | passphrase.zeroize(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 216 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 217 | let inner = runtime.block_on(InnerMachine::with_store( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 218 | &user_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 219 | device_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 220 | Arc::new(store), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 221 | None, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 222 | ))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 223 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 224 | Ok(Arc::new(OlmMachine { inner: ManuallyDrop::new(inner), runtime })) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 225 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 226 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 227 | /// Get the user ID of the owner of this `OlmMachine`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 228 | pub fn user_id(&self) -> String { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 229 | self.inner.user_id().to_string() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 230 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 231 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 232 | /// Get the device ID of the device of this `OlmMachine`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 233 | pub fn device_id(&self) -> String { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 234 | self.inner.device_id().to_string() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 235 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 236 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 237 | /// Get our own identity keys. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 238 | pub fn identity_keys(&self) -> HashMap<String, String> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 239 | let identity_keys = self.inner.identity_keys(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 240 | let curve_key = identity_keys.curve25519.to_base64(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 241 | let ed25519_key = identity_keys.ed25519.to_base64(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 242 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 243 | HashMap::from([("ed25519".to_owned(), ed25519_key), ("curve25519".to_owned(), curve_key)]) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 244 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 245 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 246 | /// Get the status of the private cross signing keys. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 247 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 248 | /// This can be used to check which private cross signing keys we have |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 249 | /// stored locally. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 250 | pub fn cross_signing_status(&self) -> CrossSigningStatus { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 251 | self.runtime.block_on(self.inner.cross_signing_status()).into() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 252 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 253 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 254 | /// Get a cross signing user identity for the given user ID. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 255 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 256 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 257 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 258 | /// * `user_id` - The unique id of the user that the identity belongs to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 259 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 260 | /// * `timeout` - The time in seconds we should wait before returning if the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 261 | /// user's device list has been marked as stale. Passing a 0 as the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 262 | /// timeout means that we won't wait at all. **Note**, this assumes that |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 263 | /// the requests from [`OlmMachine::outgoing_requests`] are being |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 264 | /// processed and sent out. Namely, this waits for a `/keys/query` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 265 | /// response to be received. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 266 | pub fn get_identity( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 267 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 268 | user_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 269 | timeout: u32, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 270 | ) -> Result<Option<UserIdentity>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 271 | let user_id = parse_user_id(&user_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 272 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 273 | let timeout = if timeout == 0 { None } else { Some(Duration::from_secs(timeout.into())) }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 274 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 275 | Ok( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 276 | if let Some(identity) = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 277 | self.runtime.block_on(self.inner.get_identity(&user_id, timeout))? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 278 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 279 | Some(self.runtime.block_on(UserIdentity::from_rust(identity))?) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 280 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 281 | None |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 282 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 283 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 284 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 285 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 286 | /// Check if a user identity is considered to be verified by us. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 287 | pub fn is_identity_verified(&self, user_id: String) -> Result<bool, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 288 | let user_id = parse_user_id(&user_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 289 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 290 | Ok( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 291 | if let Some(identity) = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 292 | self.runtime.block_on(self.inner.get_identity(&user_id, None))? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 293 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 294 | identity.is_verified() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 295 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 296 | false |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 297 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 298 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 299 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 300 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 301 | /// Manually the user with the given user ID. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 302 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 303 | /// This method will attempt to sign the user identity using either our |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 304 | /// private cross signing key, for other user identities, or our device keys |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 305 | /// for our own user identity. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 306 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 307 | /// This method can fail if we don't have the private part of our |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 308 | /// user-signing key. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 309 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 310 | /// Returns a request that needs to be sent out for the user identity to be |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 311 | /// marked as verified. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 312 | pub fn verify_identity( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 313 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 314 | user_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 315 | ) -> Result<SignatureUploadRequest, SignatureError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 316 | let user_id = UserId::parse(user_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 317 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 318 | let user_identity = self.runtime.block_on(self.inner.get_identity(&user_id, None))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 319 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 320 | if let Some(user_identity) = user_identity { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 321 | Ok(match user_identity { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 322 | SdkUserIdentity::Own(i) => self.runtime.block_on(i.verify())?, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 323 | SdkUserIdentity::Other(i) => self.runtime.block_on(i.verify())?, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 324 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 325 | .into()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 326 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 327 | Err(SignatureError::UnknownUserIdentity(user_id.to_string())) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 328 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 329 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 330 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 331 | /// Get a `Device` from the store. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 332 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 333 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 334 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 335 | /// * `user_id` - The id of the device owner. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 336 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 337 | /// * `device_id` - The id of the device itself. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 338 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 339 | /// * `timeout` - The time in seconds we should wait before returning if the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 340 | /// user's device list has been marked as stale. Passing a 0 as the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 341 | /// timeout means that we won't wait at all. **Note**, this assumes that |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 342 | /// the requests from [`OlmMachine::outgoing_requests`] are being |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 343 | /// processed and sent out. Namely, this waits for a `/keys/query` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 344 | /// response to be received. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 345 | pub fn get_device( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 346 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 347 | user_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 348 | device_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 349 | timeout: u32, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 350 | ) -> Result<Option<Device>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 351 | let user_id = parse_user_id(&user_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 352 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 353 | let timeout = if timeout == 0 { None } else { Some(Duration::from_secs(timeout.into())) }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 354 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 355 | Ok(self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 356 | .runtime |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 357 | .block_on(self.inner.get_device(&user_id, device_id.as_str().into(), timeout))? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 358 | .map(|d| d.into())) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 359 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 360 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 361 | /// Manually verify the device of the given user with the given device ID. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 362 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 363 | /// This method will attempt to sign the device using our private cross |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 364 | /// signing key. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 365 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 366 | /// This method will always fail if the device belongs to someone else, we |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 367 | /// can only sign our own devices. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 368 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 369 | /// It can also fail if we don't have the private part of our self-signing |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 370 | /// key. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 371 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 372 | /// Returns a request that needs to be sent out for the device to be marked |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 373 | /// as verified. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 374 | pub fn verify_device( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 375 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 376 | user_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 377 | device_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 378 | ) -> Result<SignatureUploadRequest, SignatureError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 379 | let user_id = UserId::parse(user_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 380 | let device = self.runtime.block_on(self.inner.get_device( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 381 | &user_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 382 | device_id.as_str().into(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 383 | None, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 384 | ))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 385 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 386 | if let Some(device) = device { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 387 | Ok(self.runtime.block_on(device.verify())?.into()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 388 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 389 | Err(SignatureError::UnknownDevice(user_id, device_id)) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 390 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 391 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 392 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 393 | /// Set local trust state for the device of the given user without creating |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 394 | /// or uploading any signatures if verified |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 395 | pub fn set_local_trust( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 396 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 397 | user_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 398 | device_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 399 | trust_state: LocalTrust, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 400 | ) -> Result<(), CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 401 | let user_id = parse_user_id(&user_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 402 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 403 | let device = self.runtime.block_on(self.inner.get_device( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 404 | &user_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 405 | device_id.as_str().into(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 406 | None, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 407 | ))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 408 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 409 | if let Some(device) = device { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 410 | self.runtime.block_on(device.set_local_trust(trust_state))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 411 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 412 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 413 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 414 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 415 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 416 | /// Get all devices of an user. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 417 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 418 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 419 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 420 | /// * `user_id` - The id of the device owner. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 421 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 422 | /// * `timeout` - The time in seconds we should wait before returning if the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 423 | /// user's device list has been marked as stale. Passing a 0 as the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 424 | /// timeout means that we won't wait at all. **Note**, this assumes that |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 425 | /// the requests from [`OlmMachine::outgoing_requests`] are being |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 426 | /// processed and sent out. Namely, this waits for a `/keys/query` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 427 | /// response to be received. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 428 | pub fn get_user_devices( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 429 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 430 | user_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 431 | timeout: u32, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 432 | ) -> Result<Vec<Device>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 433 | let user_id = parse_user_id(&user_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 434 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 435 | let timeout = if timeout == 0 { None } else { Some(Duration::from_secs(timeout.into())) }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 436 | Ok(self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 437 | .runtime |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 438 | .block_on(self.inner.get_user_devices(&user_id, timeout))? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 439 | .devices() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 440 | .map(|d| d.into()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 441 | .collect()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 442 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 443 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 444 | /// Get the list of outgoing requests that need to be sent to the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 445 | /// homeserver. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 446 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 447 | /// After the request was sent out and a successful response was received |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 448 | /// the response body should be passed back to the state machine using the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 449 | /// [mark_request_as_sent()](Self::mark_request_as_sent) method. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 450 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 451 | /// **Note**: This method call should be locked per call. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 452 | pub fn outgoing_requests(&self) -> Result<Vec<Request>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 453 | Ok(self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 454 | .runtime |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 455 | .block_on(self.inner.outgoing_requests())? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 456 | .into_iter() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 457 | .map(|r| r.into()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 458 | .collect()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 459 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 460 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 461 | /// Mark a request that was sent to the server as sent. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 462 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 463 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 464 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 465 | /// * `request_id` - The unique ID of the request that was sent out. This |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 466 | /// needs to be an UUID. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 467 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 468 | /// * `request_type` - The type of the request that was sent out. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 469 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 470 | /// * `response_body` - The body of the response that was received. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 471 | pub fn mark_request_as_sent( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 472 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 473 | request_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 474 | request_type: RequestType, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 475 | response_body: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 476 | ) -> Result<(), CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 477 | let id: OwnedTransactionId = request_id.into(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 478 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 479 | let response = response_from_string(&response_body); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 480 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 481 | let response: OwnedResponse = match request_type { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 482 | RequestType::KeysUpload => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 483 | KeysUploadResponse::try_from_http_response(response).map(Into::into) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 484 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 485 | RequestType::KeysQuery => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 486 | KeysQueryResponse::try_from_http_response(response).map(Into::into) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 487 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 488 | RequestType::ToDevice => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 489 | ToDeviceResponse::try_from_http_response(response).map(Into::into) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 490 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 491 | RequestType::KeysClaim => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 492 | KeysClaimResponse::try_from_http_response(response).map(Into::into) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 493 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 494 | RequestType::SignatureUpload => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 495 | SignatureUploadResponse::try_from_http_response(response).map(Into::into) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 496 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 497 | RequestType::KeysBackup => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 498 | KeysBackupResponse::try_from_http_response(response).map(Into::into) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 499 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 500 | RequestType::RoomMessage => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 501 | RoomMessageResponse::try_from_http_response(response).map(Into::into) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 502 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 503 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 504 | .expect("Can't convert json string to response"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 505 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 506 | self.runtime.block_on(self.inner.mark_request_as_sent(&id, &response))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 507 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 508 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 509 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 510 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 511 | /// Let the state machine know about E2EE related sync changes that we |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 512 | /// received from the server. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 513 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 514 | /// This needs to be called after every sync, ideally before processing |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 515 | /// any other sync changes. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 516 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 517 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 518 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 519 | /// * `events` - A serialized array of to-device events we received in the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 520 | /// current sync response. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 521 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 522 | /// * `device_changes` - The list of devices that have changed in some way |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 523 | /// since the previous sync. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 524 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 525 | /// * `key_counts` - The map of uploaded one-time key types and counts. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 526 | pub fn receive_sync_changes( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 527 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 528 | events: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 529 | device_changes: DeviceLists, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 530 | key_counts: HashMap<String, i32>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 531 | unused_fallback_keys: Option<Vec<String>>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 532 | next_batch_token: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 533 | decryption_settings: &DecryptionSettings, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 534 | ) -> Result<SyncChangesResult, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 535 | let to_device: ToDevice = serde_json::from_str(&events)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 536 | let device_changes: RumaDeviceLists = device_changes.into(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 537 | let key_counts: BTreeMap<OneTimeKeyAlgorithm, UInt> = key_counts |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 538 | .into_iter() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 539 | .map(|(k, v)| { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 540 | ( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 541 | OneTimeKeyAlgorithm::from(k), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 542 | v.clamp(0, i32::MAX) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 543 | .try_into() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 544 | .expect("Couldn't convert key counts into an UInt"), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 545 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 546 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 547 | .collect(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 548 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 549 | let unused_fallback_keys: Option<Vec<OneTimeKeyAlgorithm>> = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 550 | unused_fallback_keys.map(|u| u.into_iter().map(OneTimeKeyAlgorithm::from).collect()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 551 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 552 | let (to_device_events, room_key_infos) = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 553 | self.runtime.block_on(self.inner.receive_sync_changes( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 554 | matrix_sdk_crypto::EncryptionSyncChanges { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 555 | to_device_events: to_device.events, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 556 | changed_devices: &device_changes, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 557 | one_time_keys_counts: &key_counts, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 558 | unused_fallback_keys: unused_fallback_keys.as_deref(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 559 | next_batch_token: Some(next_batch_token), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 560 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 561 | decryption_settings, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 562 | ))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 563 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 564 | let to_device_events = to_device_events |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 565 | .into_iter() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 566 | .map(|event| event.to_raw().json().get().to_owned()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 567 | .collect(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 568 | let room_key_infos = room_key_infos.into_iter().map(|info| info.into()).collect(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 569 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 570 | Ok(SyncChangesResult { to_device_events, room_key_infos }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 571 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 572 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 573 | /// Add the given list of users to be tracked, triggering a key query |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 574 | /// request for them. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 575 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 576 | /// The OlmMachine maintains a list of users whose devices we are keeping |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 577 | /// track of: these are known as "tracked users". These must be users |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 578 | /// that we share a room with, so that the server sends us updates for |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 579 | /// their device lists. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 580 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 581 | /// *Note*: Only users that aren't already tracked will be considered for an |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 582 | /// update. It's safe to call this with already tracked users, it won't |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 583 | /// result in excessive `/keys/query` requests. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 584 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 585 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 586 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 587 | /// `users` - The users that should be queued up for a key query. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 588 | pub fn update_tracked_users(&self, users: Vec<String>) -> Result<(), CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 589 | let users: Vec<OwnedUserId> = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 590 | users.into_iter().filter_map(|u| UserId::parse(u).ok()).collect(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 591 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 592 | self.runtime.block_on(self.inner.update_tracked_users(users.iter().map(Deref::deref)))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 593 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 594 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 595 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 596 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 597 | /// Check if the given user is considered to be tracked. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 598 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 599 | /// A user can be marked for tracking using the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 600 | /// [`OlmMachine::update_tracked_users()`] method. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 601 | pub fn is_user_tracked(&self, user_id: String) -> Result<bool, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 602 | let user_id = parse_user_id(&user_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 603 | Ok(self.runtime.block_on(self.inner.tracked_users())?.contains(&user_id)) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 604 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 605 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 606 | /// Generate one-time key claiming requests for all the users we are missing |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 607 | /// sessions for. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 608 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 609 | /// After the request was sent out and a successful response was received |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 610 | /// the response body should be passed back to the state machine using the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 611 | /// [mark_request_as_sent()](Self::mark_request_as_sent) method. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 612 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 613 | /// This method should be called every time before a call to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 614 | /// [`share_room_key()`](Self::share_room_key) is made. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 615 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 616 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 617 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 618 | /// * `users` - The list of users for which we would like to establish 1:1 |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 619 | /// Olm sessions for. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 620 | pub fn get_missing_sessions( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 621 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 622 | users: Vec<String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 623 | ) -> Result<Option<Request>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 624 | let users: Vec<OwnedUserId> = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 625 | users.into_iter().filter_map(|u| UserId::parse(u).ok()).collect(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 626 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 627 | Ok(self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 628 | .runtime |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 629 | .block_on(self.inner.get_missing_sessions(users.iter().map(Deref::deref)))? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 630 | .map(|r| r.into())) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 631 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 632 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 633 | /// Get the stored room settings, such as the encryption algorithm or |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 634 | /// whether to encrypt only for trusted devices. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 635 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 636 | /// These settings can be modified via |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 637 | /// [set_room_algorithm()](Self::set_room_algorithm) and |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 638 | /// [set_room_only_allow_trusted_devices()](Self::set_room_only_allow_trusted_devices) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 639 | /// methods. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 640 | pub fn get_room_settings( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 641 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 642 | room_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 643 | ) -> Result<Option<RoomSettings>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 644 | let room_id = RoomId::parse(room_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 645 | let settings = self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 646 | .runtime |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 647 | .block_on(self.inner.store().get_room_settings(&room_id))? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 648 | .map(|v| v.try_into()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 649 | .transpose()?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 650 | Ok(settings) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 651 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 652 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 653 | /// Set the room algorithm used for encrypting messages to one of the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 654 | /// available variants |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 655 | pub fn set_room_algorithm( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 656 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 657 | room_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 658 | algorithm: EventEncryptionAlgorithm, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 659 | ) -> Result<(), CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 660 | let room_id = RoomId::parse(room_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 661 | self.runtime.block_on(async move { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 662 | let mut settings = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 663 | self.inner.store().get_room_settings(&room_id).await?.unwrap_or_default(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 664 | settings.algorithm = algorithm.into(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 665 | self.inner |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 666 | .store() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 667 | .save_changes(Changes { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 668 | room_settings: HashMap::from([(room_id, settings)]), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 669 | ..Default::default() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 670 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 671 | .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 672 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 673 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 674 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 675 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 676 | /// Set flag whether this room should encrypt messages for untrusted |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 677 | /// devices, or whether they should be excluded from the conversation. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 678 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 679 | /// Note that per-room setting may be overridden by a global |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 680 | /// [set_only_allow_trusted_devices()](Self::set_only_allow_trusted_devices) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 681 | /// method. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 682 | pub fn set_room_only_allow_trusted_devices( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 683 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 684 | room_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 685 | only_allow_trusted_devices: bool, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 686 | ) -> Result<(), CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 687 | let room_id = RoomId::parse(room_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 688 | self.runtime.block_on(async move { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 689 | let mut settings = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 690 | self.inner.store().get_room_settings(&room_id).await?.unwrap_or_default(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 691 | settings.only_allow_trusted_devices = only_allow_trusted_devices; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 692 | self.inner |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 693 | .store() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 694 | .save_changes(Changes { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 695 | room_settings: HashMap::from([(room_id, settings)]), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 696 | ..Default::default() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 697 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 698 | .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 699 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 700 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 701 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 702 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 703 | /// Check whether there is a global flag to only encrypt messages for |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 704 | /// trusted devices or for everyone. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 705 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 706 | /// Note that if the global flag is false, individual rooms may still be |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 707 | /// encrypting only for trusted devices, depending on the per-room |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 708 | /// `only_allow_trusted_devices` flag. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 709 | pub fn get_only_allow_trusted_devices(&self) -> Result<bool, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 710 | let block = self.runtime.block_on(self.inner.store().get_only_allow_trusted_devices())?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 711 | Ok(block) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 712 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 713 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 714 | /// Set global flag whether to encrypt messages for untrusted devices, or |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 715 | /// whether they should be excluded from the conversation. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 716 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 717 | /// Note that if enabled, it will override any per-room settings. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 718 | pub fn set_only_allow_trusted_devices( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 719 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 720 | only_allow_trusted_devices: bool, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 721 | ) -> Result<(), CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 722 | self.runtime.block_on( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 723 | self.inner.store().set_only_allow_trusted_devices(only_allow_trusted_devices), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 724 | )?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 725 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 726 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 727 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 728 | /// Share a room key with the given list of users for the given room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 729 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 730 | /// After the request was sent out and a successful response was received |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 731 | /// the response body should be passed back to the state machine using the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 732 | /// [mark_request_as_sent()](Self::mark_request_as_sent) method. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 733 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 734 | /// This method should be called every time before a call to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 735 | /// [`encrypt()`](Self::encrypt) with the given `room_id` is made. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 736 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 737 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 738 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 739 | /// * `room_id` - The unique id of the room, note that this doesn't strictly |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 740 | /// need to be a Matrix room, it just needs to be an unique identifier for |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 741 | /// the group that will participate in the conversation. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 742 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 743 | /// * `users` - The list of users which are considered to be members of the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 744 | /// room and should receive the room key. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 745 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 746 | /// * `settings` - The settings that should be used for the room key. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 747 | pub fn share_room_key( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 748 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 749 | room_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 750 | users: Vec<String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 751 | settings: EncryptionSettings, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 752 | ) -> Result<Vec<Request>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 753 | let users: Vec<OwnedUserId> = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 754 | users.into_iter().filter_map(|u| UserId::parse(u).ok()).collect(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 755 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 756 | let room_id = RoomId::parse(room_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 757 | let requests = self.runtime.block_on(self.inner.share_room_key( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 758 | &room_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 759 | users.iter().map(Deref::deref), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 760 | settings, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 761 | ))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 762 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 763 | Ok(requests.into_iter().map(|r| r.as_ref().into()).collect()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 764 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 765 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 766 | /// Encrypt the given event with the given type and content for the given |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 767 | /// room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 768 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 769 | /// **Note**: A room key needs to be shared with the group of users that are |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 770 | /// members in the given room. If this is not done this method will panic. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 771 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 772 | /// The usual flow to encrypt an event using this state machine is as |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 773 | /// follows: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 774 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 775 | /// 1. Get the one-time key claim request to establish 1:1 Olm sessions for |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 776 | /// the room members of the room we wish to participate in. This is done |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 777 | /// using the [`get_missing_sessions()`](Self::get_missing_sessions) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 778 | /// method. This method call should be locked per call. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 779 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 780 | /// 2. Share a room key with all the room members using the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 781 | /// [`share_room_key()`](Self::share_room_key). This method call should |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 782 | /// be locked per room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 783 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 784 | /// 3. Encrypt the event using this method. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 785 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 786 | /// 4. Send the encrypted event to the server. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 787 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 788 | /// After the room key is shared steps 1 and 2 will become noops, unless |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 789 | /// there's some changes in the room membership or in the list of devices a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 790 | /// member has. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 791 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 792 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 793 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 794 | /// * `room_id` - The unique id of the room where the event will be sent to. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 795 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 796 | /// * `even_type` - The type of the event. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 797 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 798 | /// * `content` - The serialized content of the event. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 799 | pub fn encrypt( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 800 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 801 | room_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 802 | event_type: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 803 | content: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 804 | ) -> Result<String, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 805 | let room_id = RoomId::parse(room_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 806 | let content = serde_json::from_str(&content)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 807 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 808 | let result = self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 809 | .runtime |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 810 | .block_on(self.inner.encrypt_room_event_raw(&room_id, &event_type, &content)) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 811 | .expect("Encrypting an event produced an error"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 812 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 813 | Ok(serde_json::to_string(&result.content)?) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 814 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 815 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 816 | /// Encrypt the given event with the given type and content for the given |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 817 | /// device. This method is used to send an event to a specific device. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 818 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 819 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 820 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 821 | /// * `user_id` - The ID of the user who owns the target device. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 822 | /// * `device_id` - The ID of the device to which the message will be sent. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 823 | /// * `event_type` - The event type. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 824 | /// * `content` - The serialized content of the event. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 825 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 826 | /// # Returns |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 827 | /// A `Result` containing the request to be sent out if the encryption was |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 828 | /// successful. If the device is not found, the result will be `Ok(None)`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 829 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 830 | /// The caller should ensure that there is an olm session (see |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 831 | /// `get_missing_sessions`) with the target device before calling this |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 832 | /// method. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 833 | pub fn create_encrypted_to_device_request( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 834 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 835 | user_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 836 | device_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 837 | event_type: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 838 | content: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 839 | share_strategy: CollectStrategy, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 840 | ) -> Result<Option<Request>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 841 | let user_id = parse_user_id(&user_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 842 | let device_id = device_id.as_str().into(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 843 | let content = serde_json::from_str(&content)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 844 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 845 | let device = self.runtime.block_on(self.inner.get_device(&user_id, device_id, None))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 846 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 847 | if let Some(device) = device { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 848 | let encrypted_content = self.runtime.block_on(device.encrypt_event_raw( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 849 | &event_type, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 850 | &content, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 851 | share_strategy, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 852 | ))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 853 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 854 | let request = ToDeviceRequest::new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 855 | user_id.as_ref(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 856 | DeviceIdOrAllDevices::DeviceId(device_id.to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 857 | "m.room.encrypted", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 858 | encrypted_content.cast(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 859 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 860 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 861 | Ok(Some(request.into())) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 862 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 863 | Ok(None) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 864 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 865 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 866 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 867 | /// Decrypt the given event that was sent in the given room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 868 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 869 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 870 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 871 | /// * `event` - The serialized encrypted version of the event. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 872 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 873 | /// * `room_id` - The unique id of the room where the event was sent to. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 874 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 875 | /// * `handle_verification_events` - if the supplied event is a verification |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 876 | /// event, use it to update the verification state. **Note**: it is |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 877 | /// recommended to avoid setting this flag to true and use the explicit |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 878 | /// [`OlmMachine::receive_verification_event`] method instead: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 879 | /// verification events sometimes need preparation before we can handle |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 880 | /// them: see the documentation for |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 881 | /// [`OlmMachine::receive_verification_event`]. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 882 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 883 | /// * `strict_shields` - If `true`, messages will be decorated with strict |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 884 | /// warnings (use `false` to match legacy behaviour where unsafe keys have |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 885 | /// lower severity warnings and unverified identities are not decorated). |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 886 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 887 | /// * `decryption_settings` - The setting for decrypting messages. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 888 | pub fn decrypt_room_event( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 889 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 890 | event: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 891 | room_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 892 | handle_verification_events: bool, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 893 | strict_shields: bool, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 894 | decryption_settings: DecryptionSettings, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 895 | ) -> Result<DecryptedEvent, DecryptionError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 896 | // Element Android wants only the content and the type and will create a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 897 | // decrypted event with those two itself, this struct makes sure we |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 898 | // throw away all the other fields. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 899 | #[derive(Deserialize, Serialize)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 900 | struct Event<'a> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 901 | #[serde(rename = "type")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 902 | event_type: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 903 | #[serde(borrow)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 904 | content: &'a RawValue, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 905 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 906 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 907 | let event: Raw<_> = serde_json::from_str(&event)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 908 | let room_id = RoomId::parse(room_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 909 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 910 | let decrypted = self.runtime.block_on(self.inner.decrypt_room_event( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 911 | &event, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 912 | &room_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 913 | &decryption_settings, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 914 | ))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 915 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 916 | if handle_verification_events |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 917 | && let Ok(AnyTimelineEvent::MessageLike(e)) = decrypted.event.deserialize() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 918 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 919 | match &e { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 920 | AnyMessageLikeEvent::RoomMessage(MessageLikeEvent::Original(original_event)) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 921 | if let MessageType::VerificationRequest(_) = &original_event.content.msgtype { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 922 | self.runtime.block_on(self.inner.receive_verification_event(&e))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 923 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 924 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 925 | _ if e.event_type().to_string().starts_with("m.key.verification") => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 926 | self.runtime.block_on(self.inner.receive_verification_event(&e))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 927 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 928 | _ => (), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 929 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 930 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 931 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 932 | let encryption_info = decrypted.encryption_info; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 933 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 934 | let event_json: Event<'_> = serde_json::from_str(decrypted.event.json().get())?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 935 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 936 | Ok(match &encryption_info.algorithm_info { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 937 | AlgorithmInfo::MegolmV1AesSha2 { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 938 | curve25519_key, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 939 | sender_claimed_keys, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 940 | session_id: _, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 941 | } => DecryptedEvent { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 942 | clear_event: serde_json::to_string(&event_json)?, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 943 | sender_curve25519_key: curve25519_key.to_owned(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 944 | claimed_ed25519_key: sender_claimed_keys.get(&DeviceKeyAlgorithm::Ed25519).cloned(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 945 | forwarding_curve25519_chain: vec![], |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 946 | shield_state: if strict_shields { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 947 | encryption_info.verification_state.to_shield_state_strict().into() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 948 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 949 | encryption_info.verification_state.to_shield_state_lax().into() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 950 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 951 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 952 | AlgorithmInfo::OlmV1Curve25519AesSha2 { .. } => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 953 | // cannot happen because `decrypt_room_event` would have fail to decrypt olm for |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 954 | // a room (EventError::UnsupportedAlgorithm) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 955 | panic!("Unsupported olm algorithm in room") |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 956 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 957 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 958 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 959 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 960 | /// Request or re-request a room key that was used to encrypt the given |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 961 | /// event. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 962 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 963 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 964 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 965 | /// * `event` - The undecryptable event that we would wish to request a room |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 966 | /// key for. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 967 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 968 | /// * `room_id` - The id of the room the event was sent to. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 969 | pub fn request_room_key( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 970 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 971 | event: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 972 | room_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 973 | ) -> Result<KeyRequestPair, DecryptionError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 974 | let event: Raw<_> = serde_json::from_str(&event)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 975 | let room_id = RoomId::parse(room_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 976 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 977 | let (cancel, request) = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 978 | self.runtime.block_on(self.inner.request_room_key(&event, &room_id))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 979 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 980 | let cancellation = cancel.map(|r| r.into()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 981 | let key_request = request.into(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 982 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 983 | Ok(KeyRequestPair { cancellation, key_request }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 984 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 985 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 986 | /// Export all of our room keys. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 987 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 988 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 989 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 990 | /// * `passphrase` - The passphrase that should be used to encrypt the key |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 991 | /// export. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 992 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 993 | /// * `rounds` - The number of rounds that should be used when expanding the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 994 | /// passphrase into an key. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 995 | pub fn export_room_keys( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 996 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 997 | passphrase: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 998 | rounds: i32, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 999 | ) -> Result<String, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1000 | let keys = self.runtime.block_on(self.inner.store().export_room_keys(|_| true))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1001 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1002 | let encrypted = encrypt_room_key_export(&keys, &passphrase, rounds as u32) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1003 | .map_err(CryptoStoreError::Serialization)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1004 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1005 | Ok(encrypted) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1006 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1007 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1008 | /// Import room keys from the given serialized key export. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1009 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1010 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1011 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1012 | /// * `keys` - The serialized version of the key export. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1013 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1014 | /// * `passphrase` - The passphrase that was used to encrypt the key export. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1015 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1016 | /// * `progress_listener` - A callback that can be used to introspect the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1017 | /// progress of the key import. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1018 | pub fn import_room_keys( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1019 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1020 | keys: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1021 | passphrase: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1022 | progress_listener: Box<dyn ProgressListener>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1023 | ) -> Result<KeysImportResult, KeyImportError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1024 | let keys = Cursor::new(keys); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1025 | let keys = decrypt_room_key_export(keys, &passphrase)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1026 | self.import_room_keys_helper(keys, None, progress_listener) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1027 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1028 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1029 | /// Import room keys from the given serialized unencrypted key export. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1030 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1031 | /// This method is the same as [`OlmMachine::import_room_keys`] but the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1032 | /// decryption step is skipped and should be performed by the caller. This |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1033 | /// should be used if the room keys are coming from the server-side backup, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1034 | /// the method will mark all imported room keys as backed up. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1035 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1036 | /// **Note**: This has been deprecated. Use |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1037 | /// [`OlmMachine::import_room_keys_from_backup`] instead. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1038 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1039 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1040 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1041 | /// * `keys` - The serialized version of the unencrypted key export. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1042 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1043 | /// * `progress_listener` - A callback that can be used to introspect the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1044 | /// progress of the key import. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1045 | pub fn import_decrypted_room_keys( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1046 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1047 | keys: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1048 | progress_listener: Box<dyn ProgressListener>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1049 | ) -> Result<KeysImportResult, KeyImportError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1050 | // Assume that the keys came from the current backup version. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1051 | let backup_version = self.runtime.block_on(self.inner.backup_machine().backup_version()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1052 | let keys: Vec<Value> = serde_json::from_str(&keys)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1053 | let keys = keys.into_iter().map(serde_json::from_value).filter_map(|k| k.ok()).collect(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1054 | self.import_room_keys_helper(keys, backup_version.as_deref(), progress_listener) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1055 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1056 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1057 | /// Import room keys from the given serialized unencrypted key export. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1058 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1059 | /// This method is the same as [`OlmMachine::import_room_keys`] but the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1060 | /// decryption step is skipped and should be performed by the caller. This |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1061 | /// should be used if the room keys are coming from the server-side backup. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1062 | /// The method will mark all imported room keys as backed up. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1063 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1064 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1065 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1066 | /// * `keys` - The serialized version of the unencrypted key export. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1067 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1068 | /// * `backup_version` - The version of the backup that these keys came |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1069 | /// from. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1070 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1071 | /// * `progress_listener` - A callback that can be used to introspect the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1072 | /// progress of the key import. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1073 | pub fn import_room_keys_from_backup( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1074 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1075 | keys: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1076 | backup_version: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1077 | progress_listener: Box<dyn ProgressListener>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1078 | ) -> Result<KeysImportResult, KeyImportError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1079 | let keys: Vec<Value> = serde_json::from_str(&keys)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1080 | let keys = keys.into_iter().map(serde_json::from_value).filter_map(|k| k.ok()).collect(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1081 | self.import_room_keys_helper(keys, Some(&backup_version), progress_listener) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1082 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1083 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1084 | /// Discard the currently active room key for the given room if there is |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1085 | /// one. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1086 | pub fn discard_room_key(&self, room_id: String) -> Result<(), CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1087 | let room_id = RoomId::parse(room_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1088 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1089 | self.runtime.block_on(self.inner.discard_room_key(&room_id))?; |
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 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1094 | /// Receive an unencrypted verification event. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1095 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1096 | /// This method can be used to pass verification events that are happening |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1097 | /// in unencrypted rooms to the `OlmMachine`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1098 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1099 | /// **Note**: This has been deprecated. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1100 | pub fn receive_unencrypted_verification_event( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1101 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1102 | event: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1103 | room_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1104 | ) -> Result<(), CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1105 | self.receive_verification_event(event, room_id) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1106 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1107 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1108 | /// Receive a verification event. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1109 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1110 | /// This method can be used to pass verification events that are happening |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1111 | /// in rooms to the `OlmMachine`. The event should be in the decrypted form. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1112 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1113 | /// **Note**: If the supplied event is an `m.room.message` event with |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1114 | /// `msgtype: m.key.verification.request`, then the device information for |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1115 | /// the sending user must be up-to-date before calling this method |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1116 | /// (otherwise, the request will be ignored). It is hard to guarantee this |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1117 | /// is the case, but you can maximize your chances by explicitly making a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1118 | /// request to /keys/query for the user's device info, and processing the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1119 | /// response with [`OlmMachine::mark_request_as_sent`]. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1120 | pub fn receive_verification_event( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1121 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1122 | event: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1123 | room_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1124 | ) -> Result<(), CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1125 | let room_id = RoomId::parse(room_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1126 | let event: AnySyncMessageLikeEvent = serde_json::from_str(&event)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1127 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1128 | let event = event.into_full_event(room_id); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1129 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1130 | self.runtime.block_on(self.inner.receive_verification_event(&event))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1131 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1132 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1133 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1134 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1135 | /// Get all the verification requests that we share with the given user. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1136 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1137 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1138 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1139 | /// * `user_id` - The ID of the user for which we would like to fetch the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1140 | /// verification requests. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1141 | pub fn get_verification_requests(&self, user_id: String) -> Vec<Arc<VerificationRequest>> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1142 | let Ok(user_id) = UserId::parse(user_id) else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1143 | return vec![]; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1144 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1145 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1146 | self.inner |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1147 | .get_verification_requests(&user_id) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1148 | .into_iter() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1149 | .map(|v| { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1150 | VerificationRequest { inner: v, runtime: self.runtime.handle().to_owned() }.into() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1151 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1152 | .collect() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1153 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1154 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1155 | /// Get a verification requests that we share with the given user with the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1156 | /// given flow id. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1157 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1158 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1159 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1160 | /// * `user_id` - The ID of the user for which we would like to fetch the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1161 | /// verification requests. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1162 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1163 | /// * `flow_id` - The ID that uniquely identifies the verification flow. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1164 | pub fn get_verification_request( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1165 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1166 | user_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1167 | flow_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1168 | ) -> Option<Arc<VerificationRequest>> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1169 | let user_id = UserId::parse(user_id).ok()?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1170 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1171 | self.inner.get_verification_request(&user_id, flow_id).map(|v| { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1172 | VerificationRequest { inner: v, runtime: self.runtime.handle().to_owned() }.into() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1173 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1174 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1175 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1176 | /// Get an m.key.verification.request content for the given user. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1177 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1178 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1179 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1180 | /// * `user_id` - The ID of the user which we would like to request to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1181 | /// verify. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1182 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1183 | /// * `methods` - The list of verification methods we want to advertise to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1184 | /// support. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1185 | pub fn verification_request_content( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1186 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1187 | user_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1188 | methods: Vec<String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1189 | ) -> Result<Option<String>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1190 | let user_id = parse_user_id(&user_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1191 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1192 | let identity = self.runtime.block_on(self.inner.get_identity(&user_id, None))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1193 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1194 | let methods = methods.into_iter().map(VerificationMethod::from).collect(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1195 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1196 | Ok(if let Some(identity) = identity.and_then(|i| i.other()) { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1197 | let content = identity.verification_request_content(Some(methods)); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1198 | Some(serde_json::to_string(&content)?) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1199 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1200 | None |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1201 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1202 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1203 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1204 | /// Request a verification flow to begin with the given user in the given |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1205 | /// room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1206 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1207 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1208 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1209 | /// * `user_id` - The ID of the user which we would like to request to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1210 | /// verify. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1211 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1212 | /// * `room_id` - The ID of the room that represents a DM with the given |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1213 | /// user. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1214 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1215 | /// * `event_id` - The event ID of the `m.key.verification.request` event |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1216 | /// that we sent out to request the verification to begin. The content for |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1217 | /// this request can be created using the [verification_request_content()] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1218 | /// method. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1219 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1220 | /// * `methods` - The list of verification methods we advertised as |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1221 | /// supported in the `m.key.verification.request` event. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1222 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1223 | /// [verification_request_content()]: Self::verification_request_content |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1224 | pub fn request_verification( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1225 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1226 | user_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1227 | room_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1228 | event_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1229 | methods: Vec<String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1230 | ) -> Result<Option<Arc<VerificationRequest>>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1231 | let user_id = parse_user_id(&user_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1232 | let event_id = EventId::parse(event_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1233 | let room_id = RoomId::parse(room_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1234 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1235 | let identity = self.runtime.block_on(self.inner.get_identity(&user_id, None))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1236 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1237 | let methods = methods.into_iter().map(VerificationMethod::from).collect(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1238 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1239 | Ok(if let Some(identity) = identity.and_then(|i| i.other()) { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1240 | let request = identity.request_verification(&room_id, &event_id, Some(methods)); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1241 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1242 | Some( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1243 | VerificationRequest { inner: request, runtime: self.runtime.handle().to_owned() } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1244 | .into(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1245 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1246 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1247 | None |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1248 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1249 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1250 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1251 | /// Request a verification flow to begin with the given user's device. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1252 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1253 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1254 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1255 | /// * `user_id` - The ID of the user which we would like to request to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1256 | /// verify. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1257 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1258 | /// * `device_id` - The ID of the device that we wish to verify. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1259 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1260 | /// * `methods` - The list of verification methods we advertised as |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1261 | /// supported in the `m.key.verification.request` event. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1262 | pub fn request_verification_with_device( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1263 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1264 | user_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1265 | device_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1266 | methods: Vec<String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1267 | ) -> Result<Option<RequestVerificationResult>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1268 | let user_id = parse_user_id(&user_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1269 | let device_id = device_id.as_str().into(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1270 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1271 | let methods = methods.into_iter().map(VerificationMethod::from).collect(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1272 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1273 | Ok( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1274 | if let Some(device) = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1275 | self.runtime.block_on(self.inner.get_device(&user_id, device_id, None))? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1276 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1277 | let (verification, request) = device.request_verification_with_methods(methods); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1278 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1279 | Some(RequestVerificationResult { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1280 | verification: VerificationRequest { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1281 | inner: verification, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1282 | runtime: self.runtime.handle().to_owned(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1283 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1284 | .into(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1285 | request: request.into(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1286 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1287 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1288 | None |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1289 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1290 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1291 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1292 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1293 | /// Request a verification flow to begin with our other devices. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1294 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1295 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1296 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1297 | /// `methods` - The list of verification methods we want to advertise to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1298 | /// support. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1299 | pub fn request_self_verification( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1300 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1301 | methods: Vec<String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1302 | ) -> Result<Option<RequestVerificationResult>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1303 | let identity = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1304 | self.runtime.block_on(self.inner.get_identity(self.inner.user_id(), None))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1305 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1306 | let methods = methods.into_iter().map(VerificationMethod::from).collect(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1307 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1308 | Ok(if let Some(identity) = identity.and_then(|i| i.own()) { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1309 | let (verification, request) = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1310 | self.runtime.block_on(identity.request_verification_with_methods(methods))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1311 | Some(RequestVerificationResult { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1312 | verification: VerificationRequest { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1313 | inner: verification, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1314 | runtime: self.runtime.handle().to_owned(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1315 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1316 | .into(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1317 | request: request.into(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1318 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1319 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1320 | None |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1321 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1322 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1323 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1324 | /// Get a verification flow object for the given user with the given flow |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1325 | /// id. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1326 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1327 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1328 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1329 | /// * `user_id` - The ID of the user for which we would like to fetch the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1330 | /// verification. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1331 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1332 | /// * `flow_id` - The ID that uniquely identifies the verification flow. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1333 | pub fn get_verification(&self, user_id: String, flow_id: String) -> Option<Arc<Verification>> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1334 | let user_id = UserId::parse(user_id).ok()?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1335 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1336 | self.inner |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1337 | .get_verification(&user_id, &flow_id) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1338 | .map(|v| Verification { inner: v, runtime: self.runtime.handle().to_owned() }.into()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1339 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1340 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1341 | /// Start short auth string verification with a device without going |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1342 | /// through a verification request first. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1343 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1344 | /// **Note**: This has been largely deprecated and the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1345 | /// [request_verification_with_device()] method should be used instead. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1346 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1347 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1348 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1349 | /// * `user_id` - The ID of the user for which we would like to start the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1350 | /// SAS verification. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1351 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1352 | /// * `device_id` - The ID of device we would like to verify. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1353 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1354 | /// [request_verification_with_device()]: Self::request_verification_with_device |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1355 | pub fn start_sas_with_device( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1356 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1357 | user_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1358 | device_id: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1359 | ) -> Result<Option<StartSasResult>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1360 | let user_id = parse_user_id(&user_id)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1361 | let device_id = device_id.as_str().into(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1362 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1363 | Ok( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1364 | if let Some(device) = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1365 | self.runtime.block_on(self.inner.get_device(&user_id, device_id, None))? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1366 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1367 | let (sas, request) = self.runtime.block_on(device.start_verification())?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1368 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1369 | Some(StartSasResult { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1370 | sas: Sas { inner: Box::new(sas), runtime: self.runtime.handle().to_owned() } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1371 | .into(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1372 | request: request.into(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1373 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1374 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1375 | None |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1376 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1377 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1378 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1379 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1380 | /// Create a new private cross signing identity and create a request to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1381 | /// upload the public part of it to the server. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1382 | pub fn bootstrap_cross_signing(&self) -> Result<BootstrapCrossSigningResult, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1383 | Ok(self.runtime.block_on(self.inner.bootstrap_cross_signing(true))?.into()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1384 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1385 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1386 | /// Export all our private cross signing keys. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1387 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1388 | /// The export will contain the seed for the ed25519 keys as a base64 |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1389 | /// encoded string. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1390 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1391 | /// This method returns `None` if we don't have any private cross signing |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1392 | /// keys. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1393 | pub fn export_cross_signing_keys( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1394 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1395 | ) -> Result<Option<CrossSigningKeyExport>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1396 | Ok(self.runtime.block_on(self.inner.export_cross_signing_keys())?.map(|e| e.into())) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1397 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1398 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1399 | /// Import our private cross signing keys. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1400 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1401 | /// The export needs to contain the seed for the ed25519 keys as a base64 |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1402 | /// encoded string. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1403 | pub fn import_cross_signing_keys( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1404 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1405 | export: CrossSigningKeyExport, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1406 | ) -> Result<(), SecretImportError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1407 | self.runtime.block_on(self.inner.import_cross_signing_keys(export.into()))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1408 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1409 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1410 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1411 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1412 | /// Export all the secrets we have in the store into a serialized |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1413 | /// SecretsBundle. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1414 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1415 | /// This method will export all the private cross-signing keys and, if |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1416 | /// available, the private part of a backup key and its accompanying |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1417 | /// version. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1418 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1419 | /// The method will fail if we don't have all three private cross-signing |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1420 | /// keys available. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1421 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1422 | /// **Warning**: Only export this and share it with a trusted recipient, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1423 | /// i.e. if an existing device is sharing this with a new device. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1424 | pub fn export_secrets_bundle(&self) -> Result<String, SecretsBundleExportError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1425 | let bundle = self.runtime.block_on(self.inner.store().export_secrets_bundle())?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1426 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1427 | Ok(serde_json::to_string(&bundle)?) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1428 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1429 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1430 | /// Request missing local secrets from our devices (cross signing private |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1431 | /// keys, megolm backup). This will ask the sdk to create outgoing |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1432 | /// request to get the missing secrets. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1433 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1434 | /// The requests will be processed as soon as `outgoing_requests()` is |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1435 | /// called to process them. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1436 | pub fn query_missing_secrets_from_other_sessions(&self) -> Result<bool, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1437 | Ok(self.runtime.block_on(self.inner.query_missing_secrets_from_other_sessions())?) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1438 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1439 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1440 | /// Activate the given backup key to be used with the given backup version. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1441 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1442 | /// **Warning**: The caller needs to make sure that the given `BackupKey` is |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1443 | /// trusted, otherwise we might be encrypting room keys that a malicious |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1444 | /// party could decrypt. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1445 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1446 | /// The [`OlmMachine::verify_backup`] method can be used to so. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1447 | pub fn enable_backup_v1( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1448 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1449 | key: MegolmV1BackupKey, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1450 | version: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1451 | ) -> Result<(), DecodeError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1452 | let backup_key = RustBackupKey::from_base64(&key.public_key)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1453 | backup_key.set_version(version); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1454 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1455 | self.runtime.block_on(self.inner.backup_machine().enable_backup_v1(backup_key))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1456 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1457 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1458 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1459 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1460 | /// Are we able to encrypt room keys. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1461 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1462 | /// This returns true if we have an active `BackupKey` and backup version |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1463 | /// registered with the state machine. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1464 | pub fn backup_enabled(&self) -> bool { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1465 | self.runtime.block_on(self.inner.backup_machine().enabled()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1466 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1467 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1468 | /// Disable and reset our backup state. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1469 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1470 | /// This will remove any pending backup request, remove the backup key and |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1471 | /// reset the backup state of each room key we have. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1472 | pub fn disable_backup(&self) -> Result<(), CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1473 | Ok(self.runtime.block_on(self.inner.backup_machine().disable_backup())?) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1474 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1475 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1476 | /// Encrypt a batch of room keys and return a request that needs to be sent |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1477 | /// out to backup the room keys. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1478 | pub fn backup_room_keys(&self) -> Result<Option<Request>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1479 | let request = self.runtime.block_on(self.inner.backup_machine().backup())?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1480 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1481 | let request = request.map(|r| r.into()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1482 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1483 | Ok(request) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1484 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1485 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1486 | /// Get the number of backed up room keys and the total number of room keys. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1487 | pub fn room_key_counts(&self) -> Result<RoomKeyCounts, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1488 | Ok(self.runtime.block_on(self.inner.backup_machine().room_key_counts())?.into()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1489 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1490 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1491 | /// Store the recovery key in the crypto store. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1492 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1493 | /// This is useful if the client wants to support gossiping of the backup |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1494 | /// key. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1495 | pub fn save_recovery_key( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1496 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1497 | key: Option<Arc<BackupRecoveryKey>>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1498 | version: Option<String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1499 | ) -> Result<(), CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1500 | let key = key.map(|k| { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1501 | // We need to clone here due to FFI limitations but RecoveryKey does |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1502 | // not want to expose clone since it's private key material. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1503 | let mut encoded = k.to_base64(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1504 | let key = BackupDecryptionKey::from_base64(&encoded) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1505 | .expect("Encoding and decoding from base64 should always work"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1506 | encoded.zeroize(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1507 | key |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1508 | }); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1509 | Ok(self.runtime.block_on(self.inner.backup_machine().save_decryption_key(key, version))?) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1510 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1511 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1512 | /// Get the backup keys we have saved in our crypto store. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1513 | pub fn get_backup_keys(&self) -> Result<Option<Arc<BackupKeys>>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1514 | Ok(self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1515 | .runtime |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1516 | .block_on(self.inner.backup_machine().get_backup_keys())? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1517 | .try_into() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1518 | .ok() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1519 | .map(Arc::new)) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1520 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1521 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1522 | /// Sign the given message using our device key and if available cross |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1523 | /// signing master key. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1524 | pub fn sign( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1525 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1526 | message: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1527 | ) -> Result<HashMap<String, HashMap<String, String>>, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1528 | Ok(self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1529 | .runtime |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1530 | .block_on(self.inner.sign(&message))? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1531 | .into_iter() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1532 | .map(|(k, v)| { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1533 | ( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1534 | k.to_string(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1535 | v.into_iter() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1536 | .map(|(k, v)| { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1537 | ( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1538 | k.to_string(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1539 | match v { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1540 | Ok(s) => s.to_base64(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1541 | Err(i) => i.source, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1542 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1543 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1544 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1545 | .collect(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1546 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1547 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1548 | .collect()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1549 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1550 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1551 | /// Check if the given backup has been verified by us or by another of our |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1552 | /// devices that we trust. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1553 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1554 | /// The `backup_info` should be a JSON encoded object with the following |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1555 | /// format: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1556 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1557 | /// ```json |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1558 | /// { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1559 | /// "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1560 | /// "auth_data": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1561 | /// "public_key":"XjhWTCjW7l59pbfx9tlCBQolfnIQWARoKOzjTOPSlWM", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1562 | /// "signatures": {} |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1563 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1564 | /// } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1565 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1566 | pub fn verify_backup( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1567 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1568 | backup_info: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1569 | ) -> Result<SignatureVerification, CryptoStoreError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1570 | let backup_info = serde_json::from_str(&backup_info)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1571 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1572 | Ok(self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1573 | .runtime |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1574 | .block_on(self.inner.backup_machine().verify_backup(backup_info, false))? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1575 | .into()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1576 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1577 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1578 | /// Manage dehydrated devices. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1579 | pub fn dehydrated_devices(&self) -> Arc<DehydratedDevices> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1580 | DehydratedDevices { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1581 | inner: ManuallyDrop::new(self.inner.dehydrated_devices()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1582 | runtime: self.runtime.handle().to_owned(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1583 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1584 | .into() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1585 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1586 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1587 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1588 | impl OlmMachine { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1589 | fn import_room_keys_helper( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1590 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1591 | keys: Vec<ExportedRoomKey>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1592 | from_backup_version: Option<&str>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1593 | progress_listener: Box<dyn ProgressListener>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1594 | ) -> Result<KeysImportResult, KeyImportError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1595 | let listener = |progress: usize, total: usize| { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1596 | progress_listener.on_progress(progress as i32, total as i32) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1597 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1598 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1599 | let result = self.runtime.block_on(self.inner.store().import_room_keys( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1600 | keys, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1601 | from_backup_version, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1602 | listener, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1603 | ))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1604 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1605 | Ok(KeysImportResult { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1606 | imported: result.imported_count as i64, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1607 | total: result.total_count as i64, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1608 | keys: result |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1609 | .keys |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1610 | .into_iter() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1611 | .map(|(r, m)| { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1612 | ( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1613 | r.to_string(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1614 | m.into_iter().map(|(s, k)| (s, k.into_iter().collect())).collect(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1615 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1616 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1617 | .collect(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1618 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1619 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1620 | } |