matrix_sdk_crypto/store/
traits.rs

1// Copyright 2023 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{collections::HashMap, fmt, sync::Arc};
16
17use async_trait::async_trait;
18use matrix_sdk_common::{AsyncTraitDeps, cross_process_lock::CrossProcessLockGeneration};
19use ruma::{
20    DeviceId, OwnedDeviceId, RoomId, TransactionId, UserId, events::secret::request::SecretName,
21};
22use vodozemac::Curve25519PublicKey;
23
24use super::{
25    CryptoStoreError, Result,
26    types::{
27        BackupKeys, Changes, DehydratedDeviceKey, PendingChanges, RoomKeyCounts, RoomSettings,
28        StoredRoomKeyBundleData, TrackedUser,
29    },
30};
31#[cfg(doc)]
32use crate::olm::SenderData;
33use crate::{
34    Account, DeviceData, GossipRequest, GossippedSecret, SecretInfo, UserIdentityData,
35    olm::{
36        InboundGroupSession, OlmMessageHash, OutboundGroupSession, PrivateCrossSigningIdentity,
37        SenderDataType, Session,
38    },
39    store::types::RoomKeyWithheldEntry,
40};
41
42/// Represents a store that the `OlmMachine` uses to store E2EE data (such as
43/// cryptographic keys).
44#[cfg_attr(target_family = "wasm", async_trait(?Send))]
45#[cfg_attr(not(target_family = "wasm"), async_trait)]
46pub trait CryptoStore: AsyncTraitDeps {
47    /// The error type used by this crypto store.
48    type Error: fmt::Debug + Into<CryptoStoreError>;
49
50    /// Load an account that was previously stored.
51    async fn load_account(&self) -> Result<Option<Account>, Self::Error>;
52
53    /// Try to load a private cross signing identity, if one is stored.
54    async fn load_identity(&self) -> Result<Option<PrivateCrossSigningIdentity>, Self::Error>;
55
56    /// Save the set of changes to the store.
57    ///
58    /// # Arguments
59    ///
60    /// * `changes` - The set of changes that should be stored.
61    async fn save_changes(&self, changes: Changes) -> Result<(), Self::Error>;
62
63    /// Save the set of changes to the store.
64    ///
65    /// This is an updated version of `save_changes` that will replace it as
66    /// #2624 makes progress.
67    ///
68    /// # Arguments
69    ///
70    /// * `changes` - The set of changes that should be stored.
71    async fn save_pending_changes(&self, changes: PendingChanges) -> Result<(), Self::Error>;
72
73    /// Save a list of inbound group sessions to the store.
74    ///
75    /// # Arguments
76    ///
77    /// * `sessions` - The sessions to be saved.
78    /// * `backed_up_to_version` - If the keys should be marked as having been
79    ///   backed up, the version of the backup.
80    ///
81    /// Note: some implementations ignore `backup_version` and assume the
82    /// current backup version, which is normally the same.
83    async fn save_inbound_group_sessions(
84        &self,
85        sessions: Vec<InboundGroupSession>,
86        backed_up_to_version: Option<&str>,
87    ) -> Result<(), Self::Error>;
88
89    /// Get all the sessions that belong to the given sender key.
90    ///
91    /// # Arguments
92    ///
93    /// * `sender_key` - The sender key that was used to establish the sessions.
94    async fn get_sessions(&self, sender_key: &str) -> Result<Option<Vec<Session>>, Self::Error>;
95
96    /// Get the inbound group session from our store.
97    ///
98    /// # Arguments
99    /// * `room_id` - The room id of the room that the session belongs to.
100    ///
101    /// * `sender_key` - The sender key that sent us the session.
102    ///
103    /// * `session_id` - The unique id of the session.
104    async fn get_inbound_group_session(
105        &self,
106        room_id: &RoomId,
107        session_id: &str,
108    ) -> Result<Option<InboundGroupSession>, Self::Error>;
109
110    /// Get withheld info for this key.
111    /// Allows to know if the session was not sent on purpose.
112    /// This only returns withheld info sent by the owner of the group session,
113    /// not the one you can get from a response to a key request from
114    /// another of your device.
115    async fn get_withheld_info(
116        &self,
117        room_id: &RoomId,
118        session_id: &str,
119    ) -> Result<Option<RoomKeyWithheldEntry>, Self::Error>;
120
121    /// Get all the sessions where we have received an `m.room_key.withheld`
122    /// event (or, post-[MSC4268], where there was a `withheld` entry in the key
123    /// bundle).
124    ///
125    /// [MSC4268]: https://github.com/matrix-org/matrix-spec-proposals/pull/4268
126    ///
127    /// # Arguments
128    /// * `room_id` - The ID of the room to return withheld sessions for.
129    async fn get_withheld_sessions_by_room_id(
130        &self,
131        room_id: &RoomId,
132    ) -> Result<Vec<RoomKeyWithheldEntry>, Self::Error>;
133
134    /// Get all the inbound group sessions we have stored.
135    async fn get_inbound_group_sessions(&self) -> Result<Vec<InboundGroupSession>, Self::Error>;
136
137    /// Get the number inbound group sessions we have and how many of them are
138    /// backed up.
139    async fn inbound_group_session_counts(
140        &self,
141        backup_version: Option<&str>,
142    ) -> Result<RoomKeyCounts, Self::Error>;
143
144    /// Get all the inbound group sessions for a given room.
145    ///
146    /// # Arguments
147    /// * `room_id` - The ID of the room to return sessions for.
148    async fn get_inbound_group_sessions_by_room_id(
149        &self,
150        room_id: &RoomId,
151    ) -> Result<Vec<InboundGroupSession>, Self::Error>;
152
153    /// Get a batch of inbound group sessions for the device with the supplied
154    /// curve key, whose sender data is of the supplied type.
155    ///
156    /// Sessions are not necessarily returned in any specific order, but the
157    /// returned batches are consistent: if this function is called repeatedly
158    /// with `after_session_id` set to the session ID from the last result
159    /// from the previous call, until an empty result is returned, then
160    /// eventually all matching sessions are returned. (New sessions that are
161    /// added in the course of iteration may or may not be returned.)
162    ///
163    /// This function is used when the device information is updated via a
164    /// `/keys/query` response and we want to update the sender data based
165    /// on the new information.
166    ///
167    /// # Arguments
168    ///
169    /// * `curve_key` - only return sessions created by the device with this
170    ///   curve key.
171    ///
172    /// * `sender_data_type` - only return sessions whose [`SenderData`] record
173    ///   is in this state.
174    ///
175    /// * `after_session_id` - return the sessions after this id, or start at
176    ///   the earliest if this is None.
177    ///
178    /// * `limit` - return a maximum of this many sessions.
179    async fn get_inbound_group_sessions_for_device_batch(
180        &self,
181        curve_key: Curve25519PublicKey,
182        sender_data_type: SenderDataType,
183        after_session_id: Option<String>,
184        limit: usize,
185    ) -> Result<Vec<InboundGroupSession>, Self::Error>;
186
187    /// Return a batch of ['InboundGroupSession'] ("room keys") that have not
188    /// yet been backed up in the supplied backup version.
189    ///
190    /// The size of the returned `Vec` is <= `limit`.
191    ///
192    /// Note: some implementations ignore `backup_version` and assume the
193    /// current backup version, which is normally the same.
194    async fn inbound_group_sessions_for_backup(
195        &self,
196        backup_version: &str,
197        limit: usize,
198    ) -> Result<Vec<InboundGroupSession>, Self::Error>;
199
200    /// Store the fact that the supplied sessions were backed up into the backup
201    /// with version `backup_version`.
202    ///
203    /// Note: some implementations ignore `backup_version` and assume the
204    /// current backup version, which is normally the same.
205    async fn mark_inbound_group_sessions_as_backed_up(
206        &self,
207        backup_version: &str,
208        room_and_session_ids: &[(&RoomId, &str)],
209    ) -> Result<(), Self::Error>;
210
211    /// Reset the backup state of all the stored inbound group sessions.
212    ///
213    /// Note: this is mostly implemented by stores that ignore the
214    /// `backup_version` argument on `inbound_group_sessions_for_backup` and
215    /// `mark_inbound_group_sessions_as_backed_up`. Implementations that
216    /// pay attention to the supplied backup version probably don't need to
217    /// update their storage when the current backup version changes, so have
218    /// empty implementations of this method.
219    async fn reset_backup_state(&self) -> Result<(), Self::Error>;
220
221    /// Get the backup keys we have stored.
222    async fn load_backup_keys(&self) -> Result<BackupKeys, Self::Error>;
223
224    /// Get the dehydrated device pickle key we have stored.
225    async fn load_dehydrated_device_pickle_key(
226        &self,
227    ) -> Result<Option<DehydratedDeviceKey>, Self::Error>;
228
229    /// Deletes the previously stored dehydrated device pickle key.
230    async fn delete_dehydrated_device_pickle_key(&self) -> Result<(), Self::Error>;
231
232    /// Get the outbound group session we have stored that is used for the
233    /// given room.
234    async fn get_outbound_group_session(
235        &self,
236        room_id: &RoomId,
237    ) -> Result<Option<OutboundGroupSession>, Self::Error>;
238
239    /// Provide the list of users whose devices we are keeping track of, and
240    /// whether they are considered dirty/outdated.
241    async fn load_tracked_users(&self) -> Result<Vec<TrackedUser>, Self::Error>;
242
243    /// Update the list of users whose devices we are keeping track of, and
244    /// whether they are considered dirty/outdated.
245    ///
246    /// Replaces any existing entry with a matching user ID.
247    async fn save_tracked_users(&self, users: &[(&UserId, bool)]) -> Result<(), Self::Error>;
248
249    /// Get the device for the given user with the given device ID.
250    ///
251    /// # Arguments
252    ///
253    /// * `user_id` - The user that the device belongs to.
254    ///
255    /// * `device_id` - The unique id of the device.
256    async fn get_device(
257        &self,
258        user_id: &UserId,
259        device_id: &DeviceId,
260    ) -> Result<Option<DeviceData>, Self::Error>;
261
262    /// Get all the devices of the given user.
263    ///
264    /// # Arguments
265    ///
266    /// * `user_id` - The user for which we should get all the devices.
267    async fn get_user_devices(
268        &self,
269        user_id: &UserId,
270    ) -> Result<HashMap<OwnedDeviceId, DeviceData>, Self::Error>;
271
272    /// Get the device for the current client.
273    ///
274    /// Since our own device is set when the store is created, this will always
275    /// return a device (unless there is an error).
276    async fn get_own_device(&self) -> Result<DeviceData, Self::Error>;
277
278    /// Get the user identity that is attached to the given user id.
279    ///
280    /// # Arguments
281    ///
282    /// * `user_id` - The user for which we should get the identity.
283    async fn get_user_identity(
284        &self,
285        user_id: &UserId,
286    ) -> Result<Option<UserIdentityData>, Self::Error>;
287
288    /// Check if a hash for an Olm message stored in the database.
289    async fn is_message_known(&self, message_hash: &OlmMessageHash) -> Result<bool, Self::Error>;
290
291    /// Get an outgoing secret request that we created that matches the given
292    /// request id.
293    ///
294    /// # Arguments
295    ///
296    /// * `request_id` - The unique request id that identifies this outgoing
297    /// secret request.
298    async fn get_outgoing_secret_requests(
299        &self,
300        request_id: &TransactionId,
301    ) -> Result<Option<GossipRequest>, Self::Error>;
302
303    /// Get an outgoing key request that we created that matches the given
304    /// requested key info.
305    ///
306    /// # Arguments
307    ///
308    /// * `key_info` - The key info of an outgoing secret request.
309    async fn get_secret_request_by_info(
310        &self,
311        secret_info: &SecretInfo,
312    ) -> Result<Option<GossipRequest>, Self::Error>;
313
314    /// Get all outgoing secret requests that we have in the store.
315    async fn get_unsent_secret_requests(&self) -> Result<Vec<GossipRequest>, Self::Error>;
316
317    /// Delete an outgoing key request that we created that matches the given
318    /// request id.
319    ///
320    /// # Arguments
321    ///
322    /// * `request_id` - The unique request id that identifies this outgoing key
323    /// request.
324    async fn delete_outgoing_secret_requests(
325        &self,
326        request_id: &TransactionId,
327    ) -> Result<(), Self::Error>;
328
329    /// Get all the secrets with the given [`SecretName`] we have currently
330    /// stored.
331    async fn get_secrets_from_inbox(
332        &self,
333        secret_name: &SecretName,
334    ) -> Result<Vec<GossippedSecret>, Self::Error>;
335
336    /// Delete all the secrets with the given [`SecretName`] we have currently
337    /// stored.
338    async fn delete_secrets_from_inbox(&self, secret_name: &SecretName) -> Result<(), Self::Error>;
339
340    /// Get the room settings, such as the encryption algorithm or whether to
341    /// encrypt only for trusted devices.
342    ///
343    /// # Arguments
344    ///
345    /// * `room_id` - The room id of the room
346    async fn get_room_settings(
347        &self,
348        room_id: &RoomId,
349    ) -> Result<Option<RoomSettings>, Self::Error>;
350
351    /// Get the details about the room key bundle data received from the given
352    /// user for the given room.
353    async fn get_received_room_key_bundle_data(
354        &self,
355        room_id: &RoomId,
356        user_id: &UserId,
357    ) -> Result<Option<StoredRoomKeyBundleData>, Self::Error>;
358
359    /// Get whether we have previously downloaded all room keys for a particular
360    /// room from the key backup in advance of building a room key bundle.
361    async fn has_downloaded_all_room_keys(&self, room_id: &RoomId) -> Result<bool, Self::Error>;
362
363    /// Get arbitrary data from the store
364    ///
365    /// # Arguments
366    ///
367    /// * `key` - The key to fetch data for
368    async fn get_custom_value(&self, key: &str) -> Result<Option<Vec<u8>>, Self::Error>;
369
370    /// Put arbitrary data into the store
371    ///
372    /// # Arguments
373    ///
374    /// * `key` - The key to insert data into
375    ///
376    /// * `value` - The value to insert
377    async fn set_custom_value(&self, key: &str, value: Vec<u8>) -> Result<(), Self::Error>;
378
379    /// Remove arbitrary data into the store
380    ///
381    /// # Arguments
382    ///
383    /// * `key` - The key to insert data into
384    async fn remove_custom_value(&self, key: &str) -> Result<(), Self::Error>;
385
386    /// Try to take a leased lock.
387    ///
388    /// This attempts to take a lock for the given lease duration.
389    ///
390    /// - If we already had the lease, this will extend the lease.
391    /// - If we didn't, but the previous lease has expired, we will acquire the
392    ///   lock.
393    /// - If there was no previous lease, we will acquire the lock.
394    /// - Otherwise, we don't get the lock.
395    ///
396    /// Returns whether taking the lock succeeded.
397    async fn try_take_leased_lock(
398        &self,
399        lease_duration_ms: u32,
400        key: &str,
401        holder: &str,
402    ) -> Result<Option<CrossProcessLockGeneration>, Self::Error>;
403
404    /// Load the next-batch token for a to-device query, if any.
405    async fn next_batch_token(&self) -> Result<Option<String>, Self::Error>;
406
407    /// Returns the size of the store in bytes, if known.
408    async fn get_size(&self) -> Result<Option<usize>, Self::Error>;
409}
410
411#[repr(transparent)]
412struct EraseCryptoStoreError<T>(T);
413
414#[cfg(not(tarpaulin_include))]
415impl<T: fmt::Debug> fmt::Debug for EraseCryptoStoreError<T> {
416    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417        self.0.fmt(f)
418    }
419}
420
421#[cfg_attr(target_family = "wasm", async_trait(?Send))]
422#[cfg_attr(not(target_family = "wasm"), async_trait)]
423impl<T: CryptoStore> CryptoStore for EraseCryptoStoreError<T> {
424    type Error = CryptoStoreError;
425
426    async fn load_account(&self) -> Result<Option<Account>> {
427        self.0.load_account().await.map_err(Into::into)
428    }
429
430    async fn load_identity(&self) -> Result<Option<PrivateCrossSigningIdentity>> {
431        self.0.load_identity().await.map_err(Into::into)
432    }
433
434    async fn save_changes(&self, changes: Changes) -> Result<()> {
435        self.0.save_changes(changes).await.map_err(Into::into)
436    }
437
438    async fn save_pending_changes(&self, changes: PendingChanges) -> Result<()> {
439        self.0.save_pending_changes(changes).await.map_err(Into::into)
440    }
441
442    async fn save_inbound_group_sessions(
443        &self,
444        sessions: Vec<InboundGroupSession>,
445        backed_up_to_version: Option<&str>,
446    ) -> Result<()> {
447        self.0.save_inbound_group_sessions(sessions, backed_up_to_version).await.map_err(Into::into)
448    }
449
450    async fn get_sessions(&self, sender_key: &str) -> Result<Option<Vec<Session>>> {
451        self.0.get_sessions(sender_key).await.map_err(Into::into)
452    }
453
454    async fn get_inbound_group_session(
455        &self,
456        room_id: &RoomId,
457        session_id: &str,
458    ) -> Result<Option<InboundGroupSession>> {
459        self.0.get_inbound_group_session(room_id, session_id).await.map_err(Into::into)
460    }
461
462    async fn get_inbound_group_sessions(&self) -> Result<Vec<InboundGroupSession>> {
463        self.0.get_inbound_group_sessions().await.map_err(Into::into)
464    }
465
466    async fn get_inbound_group_sessions_by_room_id(
467        &self,
468        room_id: &RoomId,
469    ) -> Result<Vec<InboundGroupSession>> {
470        self.0.get_inbound_group_sessions_by_room_id(room_id).await.map_err(Into::into)
471    }
472
473    async fn get_inbound_group_sessions_for_device_batch(
474        &self,
475        curve_key: Curve25519PublicKey,
476        sender_data_type: SenderDataType,
477        after_session_id: Option<String>,
478        limit: usize,
479    ) -> Result<Vec<InboundGroupSession>> {
480        self.0
481            .get_inbound_group_sessions_for_device_batch(
482                curve_key,
483                sender_data_type,
484                after_session_id,
485                limit,
486            )
487            .await
488            .map_err(Into::into)
489    }
490
491    async fn inbound_group_session_counts(
492        &self,
493        backup_version: Option<&str>,
494    ) -> Result<RoomKeyCounts> {
495        self.0.inbound_group_session_counts(backup_version).await.map_err(Into::into)
496    }
497    async fn inbound_group_sessions_for_backup(
498        &self,
499        backup_version: &str,
500        limit: usize,
501    ) -> Result<Vec<InboundGroupSession>> {
502        self.0.inbound_group_sessions_for_backup(backup_version, limit).await.map_err(Into::into)
503    }
504
505    async fn mark_inbound_group_sessions_as_backed_up(
506        &self,
507        backup_version: &str,
508        room_and_session_ids: &[(&RoomId, &str)],
509    ) -> Result<()> {
510        self.0
511            .mark_inbound_group_sessions_as_backed_up(backup_version, room_and_session_ids)
512            .await
513            .map_err(Into::into)
514    }
515
516    async fn reset_backup_state(&self) -> Result<()> {
517        self.0.reset_backup_state().await.map_err(Into::into)
518    }
519
520    async fn load_backup_keys(&self) -> Result<BackupKeys> {
521        self.0.load_backup_keys().await.map_err(Into::into)
522    }
523
524    async fn load_dehydrated_device_pickle_key(&self) -> Result<Option<DehydratedDeviceKey>> {
525        self.0.load_dehydrated_device_pickle_key().await.map_err(Into::into)
526    }
527
528    async fn delete_dehydrated_device_pickle_key(&self) -> Result<(), Self::Error> {
529        self.0.delete_dehydrated_device_pickle_key().await.map_err(Into::into)
530    }
531
532    async fn get_outbound_group_session(
533        &self,
534        room_id: &RoomId,
535    ) -> Result<Option<OutboundGroupSession>> {
536        self.0.get_outbound_group_session(room_id).await.map_err(Into::into)
537    }
538
539    async fn load_tracked_users(&self) -> Result<Vec<TrackedUser>> {
540        self.0.load_tracked_users().await.map_err(Into::into)
541    }
542
543    async fn save_tracked_users(&self, users: &[(&UserId, bool)]) -> Result<()> {
544        self.0.save_tracked_users(users).await.map_err(Into::into)
545    }
546
547    async fn get_device(
548        &self,
549        user_id: &UserId,
550        device_id: &DeviceId,
551    ) -> Result<Option<DeviceData>> {
552        self.0.get_device(user_id, device_id).await.map_err(Into::into)
553    }
554
555    async fn get_user_devices(
556        &self,
557        user_id: &UserId,
558    ) -> Result<HashMap<OwnedDeviceId, DeviceData>> {
559        self.0.get_user_devices(user_id).await.map_err(Into::into)
560    }
561
562    async fn get_own_device(&self) -> Result<DeviceData> {
563        self.0.get_own_device().await.map_err(Into::into)
564    }
565
566    async fn get_user_identity(&self, user_id: &UserId) -> Result<Option<UserIdentityData>> {
567        self.0.get_user_identity(user_id).await.map_err(Into::into)
568    }
569
570    async fn is_message_known(&self, message_hash: &OlmMessageHash) -> Result<bool> {
571        self.0.is_message_known(message_hash).await.map_err(Into::into)
572    }
573
574    async fn get_outgoing_secret_requests(
575        &self,
576        request_id: &TransactionId,
577    ) -> Result<Option<GossipRequest>> {
578        self.0.get_outgoing_secret_requests(request_id).await.map_err(Into::into)
579    }
580
581    async fn get_secret_request_by_info(
582        &self,
583        secret_info: &SecretInfo,
584    ) -> Result<Option<GossipRequest>> {
585        self.0.get_secret_request_by_info(secret_info).await.map_err(Into::into)
586    }
587
588    async fn get_unsent_secret_requests(&self) -> Result<Vec<GossipRequest>> {
589        self.0.get_unsent_secret_requests().await.map_err(Into::into)
590    }
591
592    async fn delete_outgoing_secret_requests(&self, request_id: &TransactionId) -> Result<()> {
593        self.0.delete_outgoing_secret_requests(request_id).await.map_err(Into::into)
594    }
595
596    async fn get_secrets_from_inbox(
597        &self,
598        secret_name: &SecretName,
599    ) -> Result<Vec<GossippedSecret>> {
600        self.0.get_secrets_from_inbox(secret_name).await.map_err(Into::into)
601    }
602
603    async fn delete_secrets_from_inbox(&self, secret_name: &SecretName) -> Result<()> {
604        self.0.delete_secrets_from_inbox(secret_name).await.map_err(Into::into)
605    }
606
607    async fn get_withheld_info(
608        &self,
609        room_id: &RoomId,
610        session_id: &str,
611    ) -> Result<Option<RoomKeyWithheldEntry>, Self::Error> {
612        self.0.get_withheld_info(room_id, session_id).await.map_err(Into::into)
613    }
614
615    async fn get_withheld_sessions_by_room_id(
616        &self,
617        room_id: &RoomId,
618    ) -> Result<Vec<RoomKeyWithheldEntry>, Self::Error> {
619        self.0.get_withheld_sessions_by_room_id(room_id).await.map_err(Into::into)
620    }
621
622    async fn get_room_settings(&self, room_id: &RoomId) -> Result<Option<RoomSettings>> {
623        self.0.get_room_settings(room_id).await.map_err(Into::into)
624    }
625
626    async fn get_received_room_key_bundle_data(
627        &self,
628        room_id: &RoomId,
629        user_id: &UserId,
630    ) -> Result<Option<StoredRoomKeyBundleData>> {
631        self.0.get_received_room_key_bundle_data(room_id, user_id).await.map_err(Into::into)
632    }
633
634    async fn has_downloaded_all_room_keys(&self, room_id: &RoomId) -> Result<bool, Self::Error> {
635        self.0.has_downloaded_all_room_keys(room_id).await.map_err(Into::into)
636    }
637
638    async fn get_custom_value(&self, key: &str) -> Result<Option<Vec<u8>>, Self::Error> {
639        self.0.get_custom_value(key).await.map_err(Into::into)
640    }
641
642    async fn set_custom_value(&self, key: &str, value: Vec<u8>) -> Result<(), Self::Error> {
643        self.0.set_custom_value(key, value).await.map_err(Into::into)
644    }
645
646    async fn remove_custom_value(&self, key: &str) -> Result<(), Self::Error> {
647        self.0.remove_custom_value(key).await.map_err(Into::into)
648    }
649
650    async fn try_take_leased_lock(
651        &self,
652        lease_duration_ms: u32,
653        key: &str,
654        holder: &str,
655    ) -> Result<Option<CrossProcessLockGeneration>, Self::Error> {
656        self.0.try_take_leased_lock(lease_duration_ms, key, holder).await.map_err(Into::into)
657    }
658
659    async fn next_batch_token(&self) -> Result<Option<String>, Self::Error> {
660        self.0.next_batch_token().await.map_err(Into::into)
661    }
662
663    async fn get_size(&self) -> Result<Option<usize>, Self::Error> {
664        self.0.get_size().await.map_err(Into::into)
665    }
666}
667
668/// A type-erased [`CryptoStore`].
669pub type DynCryptoStore = dyn CryptoStore<Error = CryptoStoreError>;
670
671/// A type that can be type-erased into `Arc<DynCryptoStore>`.
672///
673/// This trait is not meant to be implemented directly outside
674/// `matrix-sdk-crypto`, but it is automatically implemented for everything that
675/// implements `CryptoStore`.
676pub trait IntoCryptoStore {
677    #[doc(hidden)]
678    fn into_crypto_store(self) -> Arc<DynCryptoStore>;
679}
680
681impl<T> IntoCryptoStore for T
682where
683    T: CryptoStore + 'static,
684{
685    fn into_crypto_store(self) -> Arc<DynCryptoStore> {
686        Arc::new(EraseCryptoStoreError(self))
687    }
688}
689
690// Turns a given `Arc<T>` into `Arc<DynCryptoStore>` by attaching the
691// CryptoStore impl vtable of `EraseCryptoStoreError<T>`.
692impl<T> IntoCryptoStore for Arc<T>
693where
694    T: CryptoStore + 'static,
695{
696    fn into_crypto_store(self) -> Arc<DynCryptoStore> {
697        let ptr: *const T = Arc::into_raw(self);
698        let ptr_erased = ptr as *const EraseCryptoStoreError<T>;
699        // SAFETY: EraseCryptoStoreError is repr(transparent) so T and
700        //         EraseCryptoStoreError<T> have the same layout and ABI
701        unsafe { Arc::from_raw(ptr_erased) }
702    }
703}
704
705impl IntoCryptoStore for Arc<DynCryptoStore> {
706    fn into_crypto_store(self) -> Arc<DynCryptoStore> {
707        self
708    }
709}