@@ -95,10 +95,13 @@ use futures_util::StreamExt as _;
9595use ruma:: {
9696 api:: client:: keys:: get_keys,
9797 events:: {
98- secret:: send:: ToDeviceSecretSendEvent ,
99- secret_storage:: default_key:: SecretStorageDefaultKeyEvent ,
98+ secret:: { request:: SecretName , send:: ToDeviceSecretSendEvent } ,
99+ secret_storage:: { default_key:: SecretStorageDefaultKeyEvent , secret:: SecretEventContent } ,
100+ GlobalAccountDataEventType ,
100101 } ,
102+ serde:: Raw ,
101103} ;
104+ use serde_json:: { json, value:: to_raw_value} ;
102105use tracing:: { error, info, instrument, warn} ;
103106
104107#[ cfg( doc) ]
@@ -124,6 +127,15 @@ pub struct Recovery {
124127}
125128
126129impl Recovery {
130+ /// The list of known secrets that are contained in secret storage once
131+ /// recover is enabled.
132+ pub const KNOWN_SECRETS : & [ SecretName ] = & [
133+ SecretName :: CrossSigningMasterKey ,
134+ SecretName :: CrossSigningUserSigningKey ,
135+ SecretName :: CrossSigningSelfSigningKey ,
136+ SecretName :: RecoveryKey ,
137+ ] ;
138+
127139 /// Get the current [`RecoveryState`] for this [`Client`].
128140 pub fn state ( & self ) -> RecoveryState {
129141 self . client . inner . e2ee . recovery_state . get ( )
@@ -285,11 +297,36 @@ impl Recovery {
285297 #[ instrument( skip_all) ]
286298 pub async fn disable ( & self ) -> Result < ( ) > {
287299 self . client . encryption ( ) . backups ( ) . disable ( ) . await ?;
300+
288301 // Why oh why, can't we delete account data events?
302+ //
303+ // Alright, let's attempt to "delete" the content of our current default key,
304+ // for this we first need to check if there is a default key, then
305+ // deserialize the content and find out the key ID.
306+ //
307+ // Then we finally set the event to an empty JSON content.
308+ if let Ok ( Some ( default_event) ) =
309+ self . client . encryption ( ) . secret_storage ( ) . fetch_default_key_id ( ) . await
310+ {
311+ if let Ok ( default_event) = default_event. deserialize ( ) {
312+ let key_id = default_event. key_id ;
313+ let event_type = GlobalAccountDataEventType :: SecretStorageKey ( key_id) ;
314+
315+ self . client
316+ . account ( )
317+ . set_account_data_raw ( event_type, Raw :: new ( & json ! ( { } ) ) . expect ( "" ) . cast ( ) )
318+ . await ?;
319+ }
320+ }
321+
322+ // Now let's "delete" the actual `m.secret.storage.default_key` event.
289323 self . client . account ( ) . set_account_data ( SecretStorageDisabledContent { } ) . await ?;
324+ // Make sure that we don't re-enable backups automatically.
290325 self . client . account ( ) . set_account_data ( BackupDisabledContent { disabled : true } ) . await ?;
326+ // Finally, "delete" all the known secrets we have in the account data.
327+ self . delete_all_known_secrets ( ) . await ?;
328+
291329 self . update_recovery_state ( ) . await ?;
292- // TODO: Do we want to "delete" the known secrets as well?
293330
294331 Ok ( ( ) )
295332 }
@@ -527,6 +564,28 @@ impl Recovery {
527564 Ok ( ( ) )
528565 }
529566
567+ /// Delete all the known secrets we are keeping in secret storage.
568+ ///
569+ /// The exact list of secrets is defined in [`Recovery::KNOWN_SECRETS`] and
570+ /// might change over time.
571+ ///
572+ /// Since account data events can't actually be deleted, due to a missing
573+ /// DELETE API, we're replacing the events with an empty
574+ /// [`SecretEventContent`].
575+ async fn delete_all_known_secrets ( & self ) -> Result < ( ) > {
576+ for secret_name in Self :: KNOWN_SECRETS {
577+ let event_type = GlobalAccountDataEventType :: from ( secret_name. to_owned ( ) ) ;
578+ let content = SecretEventContent :: new ( Default :: default ( ) ) ;
579+ let secret_content = Raw :: from_json (
580+ to_raw_value ( & content)
581+ . expect ( "We should be able to serialize a raw empty secret event content" ) ,
582+ ) ;
583+ self . client . account ( ) . set_account_data_raw ( event_type, secret_content) . await ?;
584+ }
585+
586+ Ok ( ( ) )
587+ }
588+
530589 /// Run a network request to figure whether backups have been disabled at
531590 /// the account level.
532591 async fn are_backups_marked_as_disabled ( & self ) -> Result < bool > {
0 commit comments