1use std::sync::Arc;
2
3use futures_util::StreamExt;
4use matrix_sdk::{
5 encryption,
6 encryption::{backups, recovery},
7};
8use thiserror::Error;
9use tracing::{error, info};
10use zeroize::Zeroize;
11
12use super::RUNTIME;
13use crate::{client::Client, error::ClientError, ruma::AuthData, task_handle::TaskHandle};
14
15#[derive(uniffi::Object)]
16pub struct Encryption {
17 pub(crate) inner: matrix_sdk::encryption::Encryption,
18
19 pub(crate) _client: Arc<Client>,
25}
26
27#[matrix_sdk_ffi_macros::export(callback_interface)]
28pub trait BackupStateListener: Sync + Send {
29 fn on_update(&self, status: BackupState);
30}
31
32#[matrix_sdk_ffi_macros::export(callback_interface)]
33pub trait BackupSteadyStateListener: Sync + Send {
34 fn on_update(&self, status: BackupUploadState);
35}
36
37#[matrix_sdk_ffi_macros::export(callback_interface)]
38pub trait RecoveryStateListener: Sync + Send {
39 fn on_update(&self, status: RecoveryState);
40}
41
42#[matrix_sdk_ffi_macros::export(callback_interface)]
43pub trait VerificationStateListener: Sync + Send {
44 fn on_update(&self, status: VerificationState);
45}
46
47#[derive(uniffi::Enum)]
48pub enum BackupUploadState {
49 Waiting,
50 Uploading { backed_up_count: u32, total_count: u32 },
51 Error,
52 Done,
53}
54
55#[derive(Debug, Error, uniffi::Error)]
56#[uniffi(flat_error)]
57pub enum SteadyStateError {
58 #[error("The backup got disabled while waiting for the room keys to be uploaded.")]
59 BackupDisabled,
60 #[error("There was a connection error.")]
61 Connection,
62 #[error("We couldn't read status updates from the upload task quickly enough.")]
63 Lagged,
64}
65
66#[derive(Debug, Error, uniffi::Error)]
67pub enum RecoveryError {
68 #[error(
71 "A backup already exists on the homeserver and the method does not allow to overwrite it"
72 )]
73 BackupExistsOnServer,
74
75 #[error(transparent)]
77 Client { source: crate::ClientError },
78
79 #[error("Error in the secret-storage subsystem: {error_message}")]
81 SecretStorage { error_message: String },
82}
83
84impl From<matrix_sdk::encryption::recovery::RecoveryError> for RecoveryError {
85 fn from(value: matrix_sdk::encryption::recovery::RecoveryError) -> Self {
86 match value {
87 recovery::RecoveryError::BackupExistsOnServer => Self::BackupExistsOnServer,
88 recovery::RecoveryError::Sdk(e) => Self::Client { source: ClientError::from(e) },
89 recovery::RecoveryError::SecretStorage(e) => {
90 Self::SecretStorage { error_message: e.to_string() }
91 }
92 }
93 }
94}
95
96pub type Result<A, E = RecoveryError> = std::result::Result<A, E>;
97
98impl From<matrix_sdk::encryption::backups::futures::SteadyStateError> for SteadyStateError {
99 fn from(value: matrix_sdk::encryption::backups::futures::SteadyStateError) -> Self {
100 match value {
101 backups::futures::SteadyStateError::BackupDisabled => Self::BackupDisabled,
102 backups::futures::SteadyStateError::Connection => Self::Connection,
103 backups::futures::SteadyStateError::Lagged => Self::Lagged,
104 }
105 }
106}
107
108#[derive(uniffi::Enum)]
109pub enum BackupState {
110 Unknown,
111 Creating,
112 Enabling,
113 Resuming,
114 Enabled,
115 Downloading,
116 Disabling,
117}
118
119impl From<backups::BackupState> for BackupState {
120 fn from(value: backups::BackupState) -> Self {
121 match value {
122 backups::BackupState::Unknown => Self::Unknown,
123 backups::BackupState::Creating => Self::Creating,
124 backups::BackupState::Enabling => Self::Enabling,
125 backups::BackupState::Resuming => Self::Resuming,
126 backups::BackupState::Enabled => Self::Enabled,
127 backups::BackupState::Downloading => Self::Downloading,
128 backups::BackupState::Disabling => Self::Disabling,
129 }
130 }
131}
132
133impl From<backups::UploadState> for BackupUploadState {
134 fn from(value: backups::UploadState) -> Self {
135 match value {
136 backups::UploadState::Idle => Self::Waiting,
137 backups::UploadState::Uploading(count) => Self::Uploading {
138 backed_up_count: count.backed_up.try_into().unwrap_or(u32::MAX),
139 total_count: count.total.try_into().unwrap_or(u32::MAX),
140 },
141 backups::UploadState::Error => Self::Error,
142 backups::UploadState::Done => Self::Done,
143 }
144 }
145}
146
147#[derive(uniffi::Enum)]
148pub enum RecoveryState {
149 Unknown,
150 Enabled,
151 Disabled,
152 Incomplete,
153}
154
155impl From<recovery::RecoveryState> for RecoveryState {
156 fn from(value: recovery::RecoveryState) -> Self {
157 match value {
158 recovery::RecoveryState::Unknown => Self::Unknown,
159 recovery::RecoveryState::Enabled => Self::Enabled,
160 recovery::RecoveryState::Disabled => Self::Disabled,
161 recovery::RecoveryState::Incomplete => Self::Incomplete,
162 }
163 }
164}
165
166#[matrix_sdk_ffi_macros::export(callback_interface)]
167pub trait EnableRecoveryProgressListener: Sync + Send {
168 fn on_update(&self, status: EnableRecoveryProgress);
169}
170
171#[derive(uniffi::Enum)]
172pub enum EnableRecoveryProgress {
173 Starting,
174 CreatingBackup,
175 CreatingRecoveryKey,
176 BackingUp { backed_up_count: u32, total_count: u32 },
177 RoomKeyUploadError,
178 Done { recovery_key: String },
179}
180
181impl From<recovery::EnableProgress> for EnableRecoveryProgress {
182 fn from(value: recovery::EnableProgress) -> Self {
183 match &value {
184 recovery::EnableProgress::Starting => Self::Starting,
185 recovery::EnableProgress::CreatingBackup => Self::CreatingBackup,
186 recovery::EnableProgress::CreatingRecoveryKey => Self::CreatingRecoveryKey,
187 recovery::EnableProgress::BackingUp(counts) => Self::BackingUp {
188 backed_up_count: counts.backed_up.try_into().unwrap_or(u32::MAX),
189 total_count: counts.backed_up.try_into().unwrap_or(u32::MAX),
190 },
191 recovery::EnableProgress::RoomKeyUploadError => Self::RoomKeyUploadError,
192 recovery::EnableProgress::Done { recovery_key } => {
193 Self::Done { recovery_key: recovery_key.to_owned() }
194 }
195 }
196 }
197}
198
199#[derive(uniffi::Enum)]
200pub enum VerificationState {
201 Unknown,
202 Verified,
203 Unverified,
204}
205
206impl From<encryption::VerificationState> for VerificationState {
207 fn from(value: encryption::VerificationState) -> Self {
208 match &value {
209 encryption::VerificationState::Unknown => Self::Unknown,
210 encryption::VerificationState::Verified => Self::Verified,
211 encryption::VerificationState::Unverified => Self::Unverified,
212 }
213 }
214}
215
216#[matrix_sdk_ffi_macros::export]
217impl Encryption {
218 pub async fn ed25519_key(&self) -> Option<String> {
221 self.inner.ed25519_key().await
222 }
223
224 pub async fn curve25519_key(&self) -> Option<String> {
227 self.inner.curve25519_key().await.map(|k| k.to_base64())
228 }
229
230 pub fn backup_state_listener(&self, listener: Box<dyn BackupStateListener>) -> Arc<TaskHandle> {
231 let mut stream = self.inner.backups().state_stream();
232
233 let stream_task = TaskHandle::new(RUNTIME.spawn(async move {
234 while let Some(state) = stream.next().await {
235 let Ok(state) = state else { continue };
236 listener.on_update(state.into());
237 }
238 }));
239
240 stream_task.into()
241 }
242
243 pub fn backup_state(&self) -> BackupState {
244 self.inner.backups().state().into()
245 }
246
247 pub async fn backup_exists_on_server(&self) -> Result<bool, ClientError> {
257 Ok(self.inner.backups().fetch_exists_on_server().await?)
258 }
259
260 pub fn recovery_state(&self) -> RecoveryState {
261 self.inner.recovery().state().into()
262 }
263
264 pub fn recovery_state_listener(
265 &self,
266 listener: Box<dyn RecoveryStateListener>,
267 ) -> Arc<TaskHandle> {
268 let mut stream = self.inner.recovery().state_stream();
269
270 let stream_task = TaskHandle::new(RUNTIME.spawn(async move {
271 while let Some(state) = stream.next().await {
272 listener.on_update(state.into());
273 }
274 }));
275
276 stream_task.into()
277 }
278
279 pub async fn enable_backups(&self) -> Result<()> {
280 Ok(self.inner.recovery().enable_backup().await?)
281 }
282
283 pub async fn is_last_device(&self) -> Result<bool> {
284 Ok(self.inner.recovery().is_last_device().await?)
285 }
286
287 pub async fn wait_for_backup_upload_steady_state(
288 &self,
289 progress_listener: Option<Box<dyn BackupSteadyStateListener>>,
290 ) -> Result<(), SteadyStateError> {
291 let backups = self.inner.backups();
292 let wait_for_steady_state = backups.wait_for_steady_state();
293
294 let task = if let Some(listener) = progress_listener {
295 let mut progress_stream = wait_for_steady_state.subscribe_to_progress();
296
297 Some(RUNTIME.spawn(async move {
298 while let Some(progress) = progress_stream.next().await {
299 let Ok(progress) = progress else { continue };
300 listener.on_update(progress.into());
301 }
302 }))
303 } else {
304 None
305 };
306
307 let result = wait_for_steady_state.await;
308
309 if let Some(task) = task {
310 task.abort();
311 }
312
313 Ok(result?)
314 }
315
316 pub async fn enable_recovery(
317 &self,
318 wait_for_backups_to_upload: bool,
319 mut passphrase: Option<String>,
320 progress_listener: Box<dyn EnableRecoveryProgressListener>,
321 ) -> Result<String> {
322 let recovery = self.inner.recovery();
323
324 let enable = if wait_for_backups_to_upload {
325 recovery.enable().wait_for_backups_to_upload()
326 } else {
327 recovery.enable()
328 };
329
330 let enable = if let Some(passphrase) = &passphrase {
331 enable.with_passphrase(passphrase)
332 } else {
333 enable
334 };
335
336 let mut progress_stream = enable.subscribe_to_progress();
337
338 let task = RUNTIME.spawn(async move {
339 while let Some(progress) = progress_stream.next().await {
340 let Ok(progress) = progress else { continue };
341 progress_listener.on_update(progress.into());
342 }
343 });
344
345 let ret = enable.await?;
346
347 task.abort();
348 passphrase.zeroize();
349
350 Ok(ret)
351 }
352
353 pub async fn disable_recovery(&self) -> Result<()> {
354 Ok(self.inner.recovery().disable().await?)
355 }
356
357 pub async fn reset_recovery_key(&self) -> Result<String> {
358 Ok(self.inner.recovery().reset_key().await?)
359 }
360
361 pub async fn recover_and_reset(&self, mut old_recovery_key: String) -> Result<String> {
362 let result = self.inner.recovery().recover_and_reset(&old_recovery_key).await;
363
364 old_recovery_key.zeroize();
365
366 Ok(result?)
367 }
368
369 pub async fn reset_identity(&self) -> Result<Option<Arc<IdentityResetHandle>>, ClientError> {
372 if let Some(reset_handle) = self
373 .inner
374 .recovery()
375 .reset_identity()
376 .await
377 .map_err(|e| ClientError::Generic { msg: e.to_string() })?
378 {
379 return Ok(Some(Arc::new(IdentityResetHandle { inner: reset_handle })));
380 }
381
382 Ok(None)
383 }
384
385 pub async fn recover(&self, mut recovery_key: String) -> Result<()> {
386 let result = self.inner.recovery().recover(&recovery_key).await;
387
388 recovery_key.zeroize();
389
390 Ok(result?)
391 }
392
393 pub fn verification_state(&self) -> VerificationState {
394 self.inner.verification_state().get().into()
395 }
396
397 pub fn verification_state_listener(
398 self: Arc<Self>,
399 listener: Box<dyn VerificationStateListener>,
400 ) -> Arc<TaskHandle> {
401 let mut subscriber = self.inner.verification_state();
402
403 Arc::new(TaskHandle::new(RUNTIME.spawn(async move {
404 while let Some(verification_state) = subscriber.next().await {
405 listener.on_update(verification_state.into());
406 }
407 })))
408 }
409
410 pub async fn wait_for_e2ee_initialization_tasks(&self) {
413 self.inner.wait_for_e2ee_initialization_tasks().await;
414 }
415
416 pub async fn user_identity(
433 &self,
434 user_id: String,
435 ) -> Result<Option<Arc<UserIdentity>>, ClientError> {
436 match self.inner.get_user_identity(user_id.as_str().try_into()?).await {
437 Ok(Some(identity)) => {
438 return Ok(Some(Arc::new(UserIdentity { inner: identity })));
439 }
440 Ok(None) => {
441 info!("No identity found in the store.");
442 }
443 Err(error) => {
444 error!("Failed fetching identity from the store: {}", error);
445 }
446 };
447
448 info!("Requesting identity from the server.");
449
450 let identity = self.inner.request_user_identity(user_id.as_str().try_into()?).await?;
451 Ok(identity.map(|identity| Arc::new(UserIdentity { inner: identity })))
452 }
453}
454
455#[derive(uniffi::Object)]
457pub struct UserIdentity {
458 inner: matrix_sdk::encryption::identities::UserIdentity,
459}
460
461#[matrix_sdk_ffi_macros::export]
462impl UserIdentity {
463 pub(crate) async fn pin(&self) -> Result<(), ClientError> {
478 Ok(self.inner.pin().await?)
479 }
480
481 pub(crate) fn master_key(&self) -> Option<String> {
488 self.inner.master_key().get_first_key().map(|k| k.to_base64())
489 }
490
491 pub fn is_verified(&self) -> bool {
496 self.inner.is_verified()
497 }
498
499 pub fn was_previously_verified(&self) -> bool {
504 self.inner.was_previously_verified()
505 }
506
507 pub(crate) async fn withdraw_verification(&self) -> Result<(), ClientError> {
513 Ok(self.inner.withdraw_verification().await?)
514 }
515
516 pub fn has_verification_violation(&self) -> bool {
518 self.inner.has_verification_violation()
519 }
520}
521
522#[derive(uniffi::Object)]
523pub struct IdentityResetHandle {
524 pub(crate) inner: matrix_sdk::encryption::recovery::IdentityResetHandle,
525}
526
527#[matrix_sdk_ffi_macros::export]
528impl IdentityResetHandle {
529 pub fn auth_type(&self) -> CrossSigningResetAuthType {
532 self.inner.auth_type().into()
533 }
534
535 pub async fn reset(&self, auth: Option<AuthData>) -> Result<(), ClientError> {
543 if let Some(auth) = auth {
544 self.inner
545 .reset(Some(auth.into()))
546 .await
547 .map_err(|e| ClientError::Generic { msg: e.to_string() })
548 } else {
549 self.inner.reset(None).await.map_err(|e| ClientError::Generic { msg: e.to_string() })
550 }
551 }
552
553 pub async fn cancel(&self) {
554 self.inner.cancel().await;
555 }
556}
557
558#[derive(uniffi::Enum)]
559pub enum CrossSigningResetAuthType {
560 Uiaa,
562 Oidc {
565 info: OidcCrossSigningResetInfo,
566 },
567}
568
569impl From<&matrix_sdk::encryption::CrossSigningResetAuthType> for CrossSigningResetAuthType {
570 fn from(value: &matrix_sdk::encryption::CrossSigningResetAuthType) -> Self {
571 match value {
572 encryption::CrossSigningResetAuthType::Uiaa(_) => Self::Uiaa,
573 encryption::CrossSigningResetAuthType::Oidc(info) => Self::Oidc { info: info.into() },
574 }
575 }
576}
577
578#[derive(uniffi::Record)]
579pub struct OidcCrossSigningResetInfo {
580 pub approval_url: String,
582}
583
584impl From<&matrix_sdk::encryption::OidcCrossSigningResetInfo> for OidcCrossSigningResetInfo {
585 fn from(value: &matrix_sdk::encryption::OidcCrossSigningResetInfo) -> Self {
586 Self { approval_url: value.approval_url.to_string() }
587 }
588}