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