matrix_sdk/encryption/recovery/
futures.rs1use std::{future::IntoFuture, pin::pin};
18
19use futures_core::Stream;
20use futures_util::StreamExt;
21use matrix_sdk_base::crypto::secret_storage::SecretStorageKey;
22use matrix_sdk_common::boxed_into_future;
23use tokio_stream::wrappers::errors::BroadcastStreamRecvError;
24use tracing::{Instrument, Span, warn};
25
26use super::{EnableProgress, Recovery, RecoveryError, Result};
27use crate::{
28 encryption::{
29 backups::UploadState,
30 secret_storage::{SecretStore, futures::KeyOrPassphrase},
31 },
32 utils::ChannelObservable,
33};
34
35#[derive(Debug)]
37pub struct Enable<'a> {
38 pub(super) recovery: &'a Recovery,
39 pub(super) progress: ChannelObservable<EnableProgress>,
40 pub(super) wait_for_backups_upload: bool,
41 pub(super) key: KeyOrPassphrase<'a>,
42 tracing_span: Span,
43}
44
45impl<'a> Enable<'a> {
46 pub(super) fn new(recovery: &'a Recovery) -> Self {
47 Self {
48 recovery,
49 progress: Default::default(),
50 wait_for_backups_upload: false,
51 key: KeyOrPassphrase::None,
52 tracing_span: Span::current(),
53 }
54 }
55
56 pub fn subscribe_to_progress(
58 &self,
59 ) -> impl Stream<Item = Result<EnableProgress, BroadcastStreamRecvError>> + use<> {
60 self.progress.subscribe()
61 }
62
63 pub fn wait_for_backups_to_upload(mut self) -> Self {
71 self.wait_for_backups_upload = true;
72
73 self
74 }
75
76 pub fn with_passphrase(mut self, passphrase: &'a str) -> Self {
80 self.key = KeyOrPassphrase::Passphrase(passphrase);
81
82 self
83 }
84
85 pub fn with_key(mut self, key: &'a SecretStorageKey) -> Self {
87 self.key = KeyOrPassphrase::Key(key);
88
89 self
90 }
91}
92
93impl<'a> IntoFuture for Enable<'a> {
94 type Output = Result<String>;
95 boxed_into_future!(extra_bounds: 'a);
96
97 fn into_future(self) -> Self::IntoFuture {
98 let Self { recovery, progress, wait_for_backups_upload, key, tracing_span } = self;
99
100 let future = async move {
101 if !recovery.client.encryption().backups().are_enabled().await {
102 if recovery.client.encryption().backups().fetch_exists_on_server().await? {
103 return Err(RecoveryError::BackupExistsOnServer);
104 } else {
105 progress.set(EnableProgress::CreatingBackup);
106 recovery.mark_backup_as_enabled().await?;
107 recovery.client.encryption().backups().create().await?;
108 }
109 }
110
111 progress.set(EnableProgress::CreatingRecoveryKey);
112
113 let secret_storage = recovery.client.encryption().secret_storage();
114
115 let create_store = match key {
116 KeyOrPassphrase::None => secret_storage.create_secret_store(),
117 KeyOrPassphrase::Key(k) => secret_storage.create_secret_store().with_key(k),
118 KeyOrPassphrase::Passphrase(p) => {
119 secret_storage.create_secret_store().with_passphrase(p)
120 }
121 };
122
123 let store: SecretStore = create_store.await?;
124
125 if wait_for_backups_upload {
126 let backups = recovery.client.encryption().backups();
127 let upload_future = backups.wait_for_steady_state();
128 let upload_progress = upload_future.subscribe_to_progress();
129
130 #[allow(unused_variables)]
131 let progress_task = matrix_sdk_common::executor::spawn({
132 let progress = progress.clone();
133 async move {
134 let mut upload_progress = pin!(upload_progress);
135
136 while let Some(update) = upload_progress.next().await {
137 match update {
138 Ok(UploadState::Uploading(count)) => {
139 progress.set(EnableProgress::BackingUp(count));
140 }
141 Ok(UploadState::Done | UploadState::Error) | Err(_) => break,
142 _ => (),
143 }
144 }
145 }
146 });
147
148 if let Err(e) = upload_future.await {
149 warn!("Couldn't upload all the room keys to the backup: {e:?}");
150 progress.set(EnableProgress::RoomKeyUploadError);
151 }
152
153 #[cfg(not(target_family = "wasm"))]
154 progress_task.abort();
155 } else {
156 recovery.client.encryption().backups().maybe_trigger_backup();
157 }
158
159 let key = store.secret_storage_key();
160
161 progress.set(EnableProgress::Done { recovery_key: key });
162 recovery.update_recovery_state().await?;
163
164 Ok(store.secret_storage_key())
165 };
166
167 Box::pin(future.instrument(tracing_span))
168 }
169}
170
171#[derive(Debug)]
173pub struct Reset<'a> {
174 pub(super) recovery: &'a Recovery,
175 pub(super) key: KeyOrPassphrase<'a>,
176 tracing_span: Span,
177}
178
179impl<'a> Reset<'a> {
180 pub(super) fn new(recovery: &'a Recovery) -> Self {
181 Self { recovery, key: KeyOrPassphrase::None, tracing_span: Span::current() }
182 }
183
184 pub fn with_passphrase(mut self, passphrase: &'a str) -> Self {
188 self.key = KeyOrPassphrase::Passphrase(passphrase);
189
190 self
191 }
192
193 pub fn with_key(mut self, key: &'a SecretStorageKey) -> Self {
195 self.key = KeyOrPassphrase::Key(key);
196
197 self
198 }
199}
200
201impl<'a> IntoFuture for Reset<'a> {
202 type Output = Result<String>;
203 boxed_into_future!(extra_bounds: 'a);
204
205 fn into_future(self) -> Self::IntoFuture {
206 let Self { recovery, key, tracing_span } = self;
207
208 let future = async move {
209 let secret_storage = recovery.client.encryption().secret_storage();
210
211 let create_store = match key {
212 KeyOrPassphrase::None => secret_storage.create_secret_store(),
213 KeyOrPassphrase::Key(k) => secret_storage.create_secret_store().with_key(k),
214 KeyOrPassphrase::Passphrase(p) => {
215 secret_storage.create_secret_store().with_passphrase(p)
216 }
217 };
218
219 let store: SecretStore = create_store.await?;
220 recovery.update_recovery_state().await?;
221
222 Ok(store.secret_storage_key())
223 };
224
225 Box::pin(future.instrument(tracing_span))
226 }
227}
228
229#[derive(Debug)]
231pub struct RecoverAndReset<'a> {
232 pub(super) recovery: &'a Recovery,
233 pub(super) old_recovery_key: &'a str,
234 pub(super) key: KeyOrPassphrase<'a>,
235 tracing_span: Span,
236}
237
238impl<'a> RecoverAndReset<'a> {
239 pub(super) fn new(recovery: &'a Recovery, old_recovery_key: &'a str) -> Self {
240 Self {
241 recovery,
242 old_recovery_key,
243 key: KeyOrPassphrase::None,
244 tracing_span: Span::current(),
245 }
246 }
247
248 pub fn with_passphrase(mut self, passphrase: &'a str) -> Self {
252 self.key = KeyOrPassphrase::Passphrase(passphrase);
253
254 self
255 }
256
257 pub fn with_key(mut self, key: &'a SecretStorageKey) -> Self {
259 self.key = KeyOrPassphrase::Key(key);
260
261 self
262 }
263}
264
265impl<'a> IntoFuture for RecoverAndReset<'a> {
266 type Output = Result<String>;
267 boxed_into_future!(extra_bounds: 'a);
268
269 fn into_future(self) -> Self::IntoFuture {
270 let Self { recovery, old_recovery_key, key, tracing_span } = self;
271
272 let future = async move {
273 recovery.recover(old_recovery_key).await?;
274
275 let reset = match key {
276 KeyOrPassphrase::None => recovery.reset_key(),
277 KeyOrPassphrase::Key(k) => recovery.reset_key().with_key(k),
278 KeyOrPassphrase::Passphrase(p) => recovery.reset_key().with_passphrase(p),
279 };
280
281 reset.await
282 };
283
284 Box::pin(future.instrument(tracing_span))
285 }
286}