atagen

matrix-rust-sdk

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