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;
19use ruma::{
20    events::secret::request::SecretName, DeviceId, OwnedDeviceId, RoomId, TransactionId, UserId,
21};
22use vodozemac::Curve25519PublicKey;
23
24use super::{
25    types::{
26        BackupKeys, Changes, DehydratedDeviceKey, PendingChanges, RoomKeyCounts, RoomSettings,
27        StoredRoomKeyBundleData, TrackedUser,
28    },
29    CryptoStoreError, Result,
30};
31#[cfg(doc)]
32use crate::olm::SenderData;
33use crate::{
34    olm::{
35        InboundGroupSession, OlmMessageHash, OutboundGroupSession, PrivateCrossSigningIdentity,
36        SenderDataType, Session,
37    },
38    types::events::room_key_withheld::RoomKeyWithheldEvent,
39    Account, DeviceData, GossipRequest, GossippedSecret, SecretInfo, UserIdentityData,
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<RoomKeyWithheldEvent>, Self::Error>;
120
121    /// Get all the inbound group sessions we have stored.
122    async fn get_inbound_group_sessions(&self) -> Result<Vec<InboundGroupSession>, Self::Error>;
123
124    /// Get the number inbound group sessions we have and how many of them are
125    /// backed up.
126    async fn inbound_group_session_counts(
127        &self,
128        backup_version: Option<&str>,
129    ) -> Result<RoomKeyCounts, Self::Error>;
130
131    /// Get a batch of inbound group sessions for the device with the supplied
132    /// curve key, whose sender data is of the supplied type.
133    ///
134    /// Sessions are not necessarily returned in any specific order, but the
135    /// returned batches are consistent: if this function is called repeatedly
136    /// with `after_session_id` set to the session ID from the last result
137    /// from the previous call, until an empty result is returned, then
138    /// eventually all matching sessions are returned. (New sessions that are
139    /// added in the course of iteration may or may not be returned.)
140    ///
141    /// This function is used when the device information is updated via a
142    /// `/keys/query` response and we want to update the sender data based
143    /// on the new information.
144    ///
145    /// # Arguments
146    ///
147    /// * `curve_key` - only return sessions created by the device with this
148    ///   curve key.
149    ///
150    /// * `sender_data_type` - only return sessions whose [`SenderData`] record
151    ///   is in this state.
152    ///
153    /// * `after_session_id` - return the sessions after this id, or start at
154    ///   the earliest if this is None.
155    ///
156    /// * `limit` - return a maximum of this many sessions.
157    async fn get_inbound_group_sessions_for_device_batch(
158        &self,
159        curve_key: Curve25519PublicKey,
160        sender_data_type: SenderDataType,
161        after_session_id: Option<String>,
162        limit: usize,
163    ) -> Result<Vec<InboundGroupSession>, Self::Error>;
164
165    /// Return a batch of ['InboundGroupSession'] ("room keys") that have not
166    /// yet been backed up in the supplied backup version.
167    ///
168    /// The size of the returned `Vec` is <= `limit`.
169    ///
170    /// Note: some implementations ignore `backup_version` and assume the
171    /// current backup version, which is normally the same.
172    async fn inbound_group_sessions_for_backup(
173        &self,
174        backup_version: &str,
175        limit: usize,
176    ) -> Result<Vec<InboundGroupSession>, Self::Error>;
177
178    /// Store the fact that the supplied sessions were backed up into the backup
179    /// with version `backup_version`.
180    ///
181    /// Note: some implementations ignore `backup_version` and assume the
182    /// current backup version, which is normally the same.
183    async fn mark_inbound_group_sessions_as_backed_up(
184        &self,
185        backup_version: &str,
186        room_and_session_ids: &[(&RoomId, &str)],
187    ) -> Result<(), Self::Error>;
188
189    /// Reset the backup state of all the stored inbound group sessions.
190    ///
191    /// Note: this is mostly implemented by stores that ignore the
192    /// `backup_version` argument on `inbound_group_sessions_for_backup` and
193    /// `mark_inbound_group_sessions_as_backed_up`. Implementations that
194    /// pay attention to the supplied backup version probably don't need to
195    /// update their storage when the current backup version changes, so have
196    /// empty implementations of this method.
197    async fn reset_backup_state(&self) -> Result<(), Self::Error>;
198
199    /// Get the backup keys we have stored.
200    async fn load_backup_keys(&self) -> Result<BackupKeys, Self::Error>;
201
202    /// Get the dehydrated device pickle key we have stored.
203    async fn load_dehydrated_device_pickle_key(
204        &self,
205    ) -> Result<Option<DehydratedDeviceKey>, Self::Error>;
206
207    /// Deletes the previously stored dehydrated device pickle key.
208    async fn delete_dehydrated_device_pickle_key(&self) -> Result<(), Self::Error>;
209
210    /// Get the outbound group session we have stored that is used for the
211    /// given room.
212    async fn get_outbound_group_session(
213        &self,
214        room_id: &RoomId,
215    ) -> Result<Option<OutboundGroupSession>, Self::Error>;
216
217    /// Provide the list of users whose devices we are keeping track of, and
218    /// whether they are considered dirty/outdated.
219    async fn load_tracked_users(&self) -> Result<Vec<TrackedUser>, Self::Error>;
220
221    /// Update the list of users whose devices we are keeping track of, and
222    /// whether they are considered dirty/outdated.
223    ///
224    /// Replaces any existing entry with a matching user ID.
225    async fn save_tracked_users(&self, users: &[(&UserId, bool)]) -> Result<(), Self::Error>;
226
227    /// Get the device for the given user with the given device ID.
228    ///
229    /// # Arguments
230    ///
231    /// * `user_id` - The user that the device belongs to.
232    ///
233    /// * `device_id` - The unique id of the device.
234    async fn get_device(
235        &self,
236        user_id: &UserId,
237        device_id: &DeviceId,
238    ) -> Result<Option<DeviceData>, Self::Error>;
239
240    /// Get all the devices of the given user.
241    ///
242    /// # Arguments
243    ///
244    /// * `user_id` - The user for which we should get all the devices.
245    async fn get_user_devices(
246        &self,
247        user_id: &UserId,
248    ) -> Result<HashMap<OwnedDeviceId, DeviceData>, Self::Error>;
249
250    /// Get the device for the current client.
251    ///
252    /// Since our own device is set when the store is created, this will always
253    /// return a device (unless there is an error).
254    async fn get_own_device(&self) -> Result<DeviceData, Self::Error>;
255
256    /// Get the user identity that is attached to the given user id.
257    ///
258    /// # Arguments
259    ///
260    /// * `user_id` - The user for which we should get the identity.
261    async fn get_user_identity(
262        &self,
263        user_id: &UserId,
264    ) -> Result<Option<UserIdentityData>, Self::Error>;
265
266    /// Check if a hash for an Olm message stored in the database.
267    async fn is_message_known(&self, message_hash: &OlmMessageHash) -> Result<bool, Self::Error>;
268
269    /// Get an outgoing secret request that we created that matches the given
270    /// request id.
271    ///
272    /// # Arguments
273    ///
274    /// * `request_id` - The unique request id that identifies this outgoing
275    /// secret request.
276    async fn get_outgoing_secret_requests(
277        &self,
278        request_id: &TransactionId,
279    ) -> Result<Option<GossipRequest>, Self::Error>;
280
281    /// Get an outgoing key request that we created that matches the given
282    /// requested key info.
283    ///
284    /// # Arguments
285    ///
286    /// * `key_info` - The key info of an outgoing secret request.
287    async fn get_secret_request_by_info(
288        &self,
289        secret_info: &SecretInfo,
290    ) -> Result<Option<GossipRequest>, Self::Error>;
291
292    /// Get all outgoing secret requests that we have in the store.
293    async fn get_unsent_secret_requests(&self) -> Result<Vec<GossipRequest>, Self::Error>;
294
295    /// Delete an outgoing key request that we created that matches the given
296    /// request id.
297    ///
298    /// # Arguments
299    ///
300    /// * `request_id` - The unique request id that identifies this outgoing key
301    /// request.
302    async fn delete_outgoing_secret_requests(
303        &self,
304        request_id: &TransactionId,
305    ) -> Result<(), Self::Error>;
306
307    /// Get all the secrets with the given [`SecretName`] we have currently
308    /// stored.
309    async fn get_secrets_from_inbox(
310        &self,
311        secret_name: &SecretName,
312    ) -> Result<Vec<GossippedSecret>, Self::Error>;
313
314    /// Delete all the secrets with the given [`SecretName`] we have currently
315    /// stored.
316    async fn delete_secrets_from_inbox(&self, secret_name: &SecretName) -> Result<(), Self::Error>;
317
318    /// Get the room settings, such as the encryption algorithm or whether to
319    /// encrypt only for trusted devices.
320    ///
321    /// # Arguments
322    ///
323    /// * `room_id` - The room id of the room
324    async fn get_room_settings(
325        &self,
326        room_id: &RoomId,
327    ) -> Result<Option<RoomSettings>, Self::Error>;
328
329    /// Get the details about the room key bundle data received from the given
330    /// user for the given room.
331    async fn get_received_room_key_bundle_data(
332        &self,
333        room_id: &RoomId,
334        user_id: &UserId,
335    ) -> Result<Option<StoredRoomKeyBundleData>, Self::Error>;
336
337    /// Get arbitrary data from the store
338    ///
339    /// # Arguments
340    ///
341    /// * `key` - The key to fetch data for
342    async fn get_custom_value(&self, key: &str) -> Result<Option<Vec<u8>>, Self::Error>;
343
344    /// Put arbitrary data into the store
345    ///
346    /// # Arguments
347    ///
348    /// * `key` - The key to insert data into
349    ///
350    /// * `value` - The value to insert
351    async fn set_custom_value(&self, key: &str, value: Vec<u8>) -> Result<(), Self::Error>;
352
353    /// Remove arbitrary data into the store
354    ///
355    /// # Arguments
356    ///
357    /// * `key` - The key to insert data into
358    async fn remove_custom_value(&self, key: &str) -> Result<(), Self::Error>;
359
360    /// Try to take a leased lock.
361    ///
362    /// This attempts to take a lock for the given lease duration.
363    ///
364    /// - If we already had the lease, this will extend the lease.
365    /// - If we didn't, but the previous lease has expired, we will acquire the
366    ///   lock.
367    /// - If there was no previous lease, we will acquire the lock.
368    /// - Otherwise, we don't get the lock.
369    ///
370    /// Returns whether taking the lock succeeded.
371    async fn try_take_leased_lock(
372        &self,
373        lease_duration_ms: u32,
374        key: &str,
375        holder: &str,
376    ) -> Result<bool, Self::Error>;
377
378    /// Load the next-batch token for a to-device query, if any.
379    async fn next_batch_token(&self) -> Result<Option<String>, Self::Error>;
380}
381
382#[repr(transparent)]
383struct EraseCryptoStoreError<T>(T);
384
385#[cfg(not(tarpaulin_include))]
386impl<T: fmt::Debug> fmt::Debug for EraseCryptoStoreError<T> {
387    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388        self.0.fmt(f)
389    }
390}
391
392#[cfg_attr(target_family = "wasm", async_trait(?Send))]
393#[cfg_attr(not(target_family = "wasm"), async_trait)]
394impl<T: CryptoStore> CryptoStore for EraseCryptoStoreError<T> {
395    type Error = CryptoStoreError;
396
397    async fn load_account(&self) -> Result<Option<Account>> {
398        self.0.load_account().await.map_err(Into::into)
399    }
400
401    async fn load_identity(&self) -> Result<Option<PrivateCrossSigningIdentity>> {
402        self.0.load_identity().await.map_err(Into::into)
403    }
404
405    async fn save_changes(&self, changes: Changes) -> Result<()> {
406        self.0.save_changes(changes).await.map_err(Into::into)
407    }
408
409    async fn save_pending_changes(&self, changes: PendingChanges) -> Result<()> {
410        self.0.save_pending_changes(changes).await.map_err(Into::into)
411    }
412
413    async fn save_inbound_group_sessions(
414        &self,
415        sessions: Vec<InboundGroupSession>,
416        backed_up_to_version: Option<&str>,
417    ) -> Result<()> {
418        self.0.save_inbound_group_sessions(sessions, backed_up_to_version).await.map_err(Into::into)
419    }
420
421    async fn get_sessions(&self, sender_key: &str) -> Result<Option<Vec<Session>>> {
422        self.0.get_sessions(sender_key).await.map_err(Into::into)
423    }
424
425    async fn get_inbound_group_session(
426        &self,
427        room_id: &RoomId,
428        session_id: &str,
429    ) -> Result<Option<InboundGroupSession>> {
430        self.0.get_inbound_group_session(room_id, session_id).await.map_err(Into::into)
431    }
432
433    async fn get_inbound_group_sessions(&self) -> Result<Vec<InboundGroupSession>> {
434        self.0.get_inbound_group_sessions().await.map_err(Into::into)
435    }
436
437    async fn get_inbound_group_sessions_for_device_batch(
438        &self,
439        curve_key: Curve25519PublicKey,
440        sender_data_type: SenderDataType,
441        after_session_id: Option<String>,
442        limit: usize,
443    ) -> Result<Vec<InboundGroupSession>> {
444        self.0
445            .get_inbound_group_sessions_for_device_batch(
446                curve_key,
447                sender_data_type,
448                after_session_id,
449                limit,
450            )
451            .await
452            .map_err(Into::into)
453    }
454
455    async fn inbound_group_session_counts(
456        &self,
457        backup_version: Option<&str>,
458    ) -> Result<RoomKeyCounts> {
459        self.0.inbound_group_session_counts(backup_version).await.map_err(Into::into)
460    }
461    async fn inbound_group_sessions_for_backup(
462        &self,
463        backup_version: &str,
464        limit: usize,
465    ) -> Result<Vec<InboundGroupSession>> {
466        self.0.inbound_group_sessions_for_backup(backup_version, limit).await.map_err(Into::into)
467    }
468
469    async fn mark_inbound_group_sessions_as_backed_up(
470        &self,
471        backup_version: &str,
472        room_and_session_ids: &[(&RoomId, &str)],
473    ) -> Result<()> {
474        self.0
475            .mark_inbound_group_sessions_as_backed_up(backup_version, room_and_session_ids)
476            .await
477            .map_err(Into::into)
478    }
479
480    async fn reset_backup_state(&self) -> Result<()> {
481        self.0.reset_backup_state().await.map_err(Into::into)
482    }
483
484    async fn load_backup_keys(&self) -> Result<BackupKeys> {
485        self.0.load_backup_keys().await.map_err(Into::into)
486    }
487
488    async fn load_dehydrated_device_pickle_key(&self) -> Result<Option<DehydratedDeviceKey>> {
489        self.0.load_dehydrated_device_pickle_key().await.map_err(Into::into)
490    }
491
492    async fn delete_dehydrated_device_pickle_key(&self) -> Result<(), Self::Error> {
493        self.0.delete_dehydrated_device_pickle_key().await.map_err(Into::into)
494    }
495
496    async fn get_outbound_group_session(
497        &self,
498        room_id: &RoomId,
499    ) -> Result<Option<OutboundGroupSession>> {
500        self.0.get_outbound_group_session(room_id).await.map_err(Into::into)
501    }
502
503    async fn load_tracked_users(&self) -> Result<Vec<TrackedUser>> {
504        self.0.load_tracked_users().await.map_err(Into::into)
505    }
506
507    async fn save_tracked_users(&self, users: &[(&UserId, bool)]) -> Result<()> {
508        self.0.save_tracked_users(users).await.map_err(Into::into)
509    }
510
511    async fn get_device(
512        &self,
513        user_id: &UserId,
514        device_id: &DeviceId,
515    ) -> Result<Option<DeviceData>> {
516        self.0.get_device(user_id, device_id).await.map_err(Into::into)
517    }
518
519    async fn get_user_devices(
520        &self,
521        user_id: &UserId,
522    ) -> Result<HashMap<OwnedDeviceId, DeviceData>> {
523        self.0.get_user_devices(user_id).await.map_err(Into::into)
524    }
525
526    async fn get_own_device(&self) -> Result<DeviceData> {
527        self.0.get_own_device().await.map_err(Into::into)
528    }
529
530    async fn get_user_identity(&self, user_id: &UserId) -> Result<Option<UserIdentityData>> {
531        self.0.get_user_identity(user_id).await.map_err(Into::into)
532    }
533
534    async fn is_message_known(&self, message_hash: &OlmMessageHash) -> Result<bool> {
535        self.0.is_message_known(message_hash).await.map_err(Into::into)
536    }
537
538    async fn get_outgoing_secret_requests(
539        &self,
540        request_id: &TransactionId,
541    ) -> Result<Option<GossipRequest>> {
542        self.0.get_outgoing_secret_requests(request_id).await.map_err(Into::into)
543    }
544
545    async fn get_secret_request_by_info(
546        &self,
547        secret_info: &SecretInfo,
548    ) -> Result<Option<GossipRequest>> {
549        self.0.get_secret_request_by_info(secret_info).await.map_err(Into::into)
550    }
551
552    async fn get_unsent_secret_requests(&self) -> Result<Vec<GossipRequest>> {
553        self.0.get_unsent_secret_requests().await.map_err(Into::into)
554    }
555
556    async fn delete_outgoing_secret_requests(&self, request_id: &TransactionId) -> Result<()> {
557        self.0.delete_outgoing_secret_requests(request_id).await.map_err(Into::into)
558    }
559
560    async fn get_secrets_from_inbox(
561        &self,
562        secret_name: &SecretName,
563    ) -> Result<Vec<GossippedSecret>> {
564        self.0.get_secrets_from_inbox(secret_name).await.map_err(Into::into)
565    }
566
567    async fn delete_secrets_from_inbox(&self, secret_name: &SecretName) -> Result<()> {
568        self.0.delete_secrets_from_inbox(secret_name).await.map_err(Into::into)
569    }
570
571    async fn get_withheld_info(
572        &self,
573        room_id: &RoomId,
574        session_id: &str,
575    ) -> Result<Option<RoomKeyWithheldEvent>, Self::Error> {
576        self.0.get_withheld_info(room_id, session_id).await.map_err(Into::into)
577    }
578
579    async fn get_room_settings(&self, room_id: &RoomId) -> Result<Option<RoomSettings>> {
580        self.0.get_room_settings(room_id).await.map_err(Into::into)
581    }
582
583    async fn get_received_room_key_bundle_data(
584        &self,
585        room_id: &RoomId,
586        user_id: &UserId,
587    ) -> Result<Option<StoredRoomKeyBundleData>> {
588        self.0.get_received_room_key_bundle_data(room_id, user_id).await.map_err(Into::into)
589    }
590
591    async fn get_custom_value(&self, key: &str) -> Result<Option<Vec<u8>>, Self::Error> {
592        self.0.get_custom_value(key).await.map_err(Into::into)
593    }
594
595    async fn set_custom_value(&self, key: &str, value: Vec<u8>) -> Result<(), Self::Error> {
596        self.0.set_custom_value(key, value).await.map_err(Into::into)
597    }
598
599    async fn remove_custom_value(&self, key: &str) -> Result<(), Self::Error> {
600        self.0.remove_custom_value(key).await.map_err(Into::into)
601    }
602
603    async fn try_take_leased_lock(
604        &self,
605        lease_duration_ms: u32,
606        key: &str,
607        holder: &str,
608    ) -> Result<bool, Self::Error> {
609        self.0.try_take_leased_lock(lease_duration_ms, key, holder).await.map_err(Into::into)
610    }
611
612    async fn next_batch_token(&self) -> Result<Option<String>, Self::Error> {
613        self.0.next_batch_token().await.map_err(Into::into)
614    }
615}
616
617/// A type-erased [`CryptoStore`].
618pub type DynCryptoStore = dyn CryptoStore<Error = CryptoStoreError>;
619
620/// A type that can be type-erased into `Arc<DynCryptoStore>`.
621///
622/// This trait is not meant to be implemented directly outside
623/// `matrix-sdk-crypto`, but it is automatically implemented for everything that
624/// implements `CryptoStore`.
625pub trait IntoCryptoStore {
626    #[doc(hidden)]
627    fn into_crypto_store(self) -> Arc<DynCryptoStore>;
628}
629
630impl<T> IntoCryptoStore for T
631where
632    T: CryptoStore + 'static,
633{
634    fn into_crypto_store(self) -> Arc<DynCryptoStore> {
635        Arc::new(EraseCryptoStoreError(self))
636    }
637}
638
639// Turns a given `Arc<T>` into `Arc<DynCryptoStore>` by attaching the
640// CryptoStore impl vtable of `EraseCryptoStoreError<T>`.
641impl<T> IntoCryptoStore for Arc<T>
642where
643    T: CryptoStore + 'static,
644{
645    fn into_crypto_store(self) -> Arc<DynCryptoStore> {
646        let ptr: *const T = Arc::into_raw(self);
647        let ptr_erased = ptr as *const EraseCryptoStoreError<T>;
648        // SAFETY: EraseCryptoStoreError is repr(transparent) so T and
649        //         EraseCryptoStoreError<T> have the same layout and ABI
650        unsafe { Arc::from_raw(ptr_erased) }
651    }
652}
653
654impl IntoCryptoStore for Arc<DynCryptoStore> {
655    fn into_crypto_store(self) -> Arc<DynCryptoStore> {
656        self
657    }
658}